/*
 * Copyright (c) 2018 - 2028. www.vtuzx.com Inc. All Rights Reserved
 */
package com.vtuzx.kingdee.xqc_api.service.impl;

import com.mongodb.client.ClientSession;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.vtuzx.core.VtuzxException;
import com.vtuzx.core.bean.dto.VtuzxMap;
import com.vtuzx.core.bean.dto.VtuzxPage;
import com.vtuzx.core.bean.dto.VtuzxToken;
import com.vtuzx.core.constant.IVtuzxConst;
import com.vtuzx.core.constant.ValueType;
import com.vtuzx.core.file.excel.VtuzxExcel;
import com.vtuzx.core.file.excel.VtuzxExcelCell;
import com.vtuzx.core.file.excel.VtuzxExcelRow;
import com.vtuzx.core.file.excel.VtuzxExcelSheet;
import com.vtuzx.core.utils.VtuzxUtil;
import com.vtuzx.fast.service.IFastSettingService;
import com.vtuzx.fast.utils.FastUtil;
import com.vtuzx.kingdee.xqc_api.service.IXqcInventoryService;
import com.vtuzx.kingdee.xqc_api.service.IXqcSaleService;
import com.vtuzx.kingdee.xqc_core.bean.VtuzxArrayList;
import com.vtuzx.kingdee.xqc_core.bean.kingdee.VtuzxKingdeeCloudClient;
import com.vtuzx.kingdee.xqc_core.bean.xqc.BillMaterial;
import com.vtuzx.kingdee.xqc_core.bean.xqc.BillMaterialBox;
import com.vtuzx.kingdee.xqc_core.constant.*;
import com.vtuzx.kingdee.xqc_core.dao.*;
import com.vtuzx.kingdee.xqc_core.service.IJiKeYunApiService;
import com.vtuzx.kingdee.xqc_core.service.IVtuzxBasicService;
import com.vtuzx.kingdee.xqc_core.service.IVtuzxCoreInventoryService;
import com.vtuzx.kingdee.xqc_core.service.IVtuzxKingdeeService;
import com.vtuzx.kingdee.xqc_core.service.impl.VtuzxCoreServiceImpl;
import com.vtuzx.web.bean.VtuzxDownloadBean;
import javafx.scene.paint.Material;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.omg.CORBA.CODESET_INCOMPATIBLE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import javax.print.Doc;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 即时库存服务
 */
@Service
public class XqcInventoryServiceImpl implements IXqcInventoryService {

    /**
     * Logger
     */
    private static final Logger _logger = LoggerFactory.getLogger(VtuzxCoreServiceImpl.class);

    /**
     * 即时库存Dao
     */
    @Autowired
    private InventoryDao inventoryDao;

    @Autowired
    private IVtuzxKingdeeService kingdeeService;

    @Autowired
    private PrdInDao prdInDao;

    @Autowired
    private PurInDetailBoxDao purInDetailBoxDao;

    /**
     * 即时库存分录Dao
     */
    @Autowired
    private InventoryDetailDao inventoryDetailDao;

    @Autowired
    IFastSettingService settingService;

    /**
     * 即时库存履历Dao
     */
    @Autowired
    private InventoryHistoryDao inventoryHistoryDao;

    /**
     * 即时库存履历分录Dao
     */
    @Autowired
    private InventoryHistoryDetailDao inventoryHistoryDetailDao;

    /**
     * 物料Dao
     */
    @Autowired
    private MaterialDao materialDao;
    /**
     * 物料Dao
     */
    @Autowired
    private MaterialBomPickDetailDao materialBomPickDetailDao;
    /**
     * 物料Dao
     */
    @Autowired
    private MaterialBomPickDao materialBomPickDao;
    /**
     * 物料Dao
     */
    @Autowired
    private MaterialBomPickDetailBoxDao materialBomPickDetailBoxDao;
    /**
     * 物料Dao
     */
    @Autowired
    private MaterialBomSupplementDao materialBomSupplementDao;
    /**
     * 物料Dao
     */
    @Autowired
    private MaterialBomSupplementDetailBoxDao materialBomSupplementDetailBoxDao;
    /**
     * 物料Dao
     */
    @Autowired
    private SaleOutDao saleOutDao;
    /**
     * 物料Dao
     */
    @Autowired
    private SaleOutDetailBoxDao saleOutDetailBoxDao;
    /**
     * 物料Dao
     */
    @Autowired
    private AllocationDao allocationDao;
    /**
     * 物料Dao
     */
    @Autowired
    private AllocationDetailBoxDao allocationDetailBoxDao;
    /**
     * 物料Dao
     */
    @Autowired
    private OtherOutDao otherOutDao;
    /**
     * 物料Dao
     */
    @Autowired
    private OtherOutDetailBoxDao otherOutDetailBoxDao;

    @Autowired
    private OtherInDetailBoxDao otherInDetailBoxDao;

    /**
     * 仓库
     */
    @Autowired
    private StockDao stockDao;

    /**
     * 仓位
     */
    @Autowired
    private StockDetailDao stockDetailDao;

    @Autowired
    private PrdInDetailBoxDao prdInDetailBoxDao;


    /**
     * 批次和日期调整表
     */
    @Autowired
    private BatchAndExpDao batchAndExpDao;

    @Autowired
    private JikeyunSaleDao jikeyunSaleDao;


    /**
     * 批次和日期调整表详细
     */
    @Autowired
    private BatchAndExpDetailDao batchAndExpDetailDao;


    /**
     * 批次和日期调整表详细
     */
    @Autowired
    private BatchAndExpDetailBoxDao batchAndExpDetailBoxDao;

    /** 库存服务 */
    @Autowired
    private IXqcInventoryService inventoryService;

    /** 托盘dao */
    @Autowired
    private TrayDao trayDao;

    /** 采购入库表Dao */
    @Autowired
    private PurInDetailDao purInDetailDao;

    /** 生产入库Dao  生产变品入库Dao  退料入库Dao*/
    @Autowired
    private PrdInDetailDao prdInDetailDao;

    /** 其他入库Dao */
    @Autowired
    private OtherInDetailDao otherInDetailDao;

    /** 生产领料Dao */
    @Autowired
    private PrdPickDao prdPickDao;

    /** 生产领料Dao */
    @Autowired
    private PrdPickDetailBoxDao prdPickDetailBoxDao;

    /** 生产领料Dao */
    @Autowired
    private PrdPickDetailDao prdPickDetailDao;

    /** 其他出库Dao */
    @Autowired
    private OtherOutDetailDao otherOutDetailDao;

    /** 销售出库Dao */
    @Autowired
    private SaleOutDetailDao saleOutDetailDao;

    /** 调拨Dao(物料级) */
    @Autowired
    private AllocationDetailDao allocationDetailDao;

    /** 采购入库 */
    @Autowired
    private PurColDao purColDao;

    /** 物料Dao */
    @Autowired
    private MaterialBomDetailDao materialBomDetailDao;

    /** 物料Dao */
    @Autowired
    private IVtuzxCoreInventoryService coreInventoryService;
    @Autowired
    private IVtuzxBasicService iVtuzxBasicService;

    @Autowired
    private IXqcSaleService iXqcSaleService;

    @Autowired
    private PrdColDetailDao prdColDetailDao;

    @Autowired
    private TraceabilityCodeDetailDao traceabilityCodeDetailDao;

    @Autowired
    private PrdColDao prdColDao;

    @Autowired
    private IJiKeYunApiService iJiKeYunApiService;

    @Autowired
    private StoreTokenDao storeTokenDao;

    @Autowired
    private PrdTaskDao prdTaskDao;

    @Autowired
    private PrdTaskDetailDao prdTaskDetailDao;

    @Autowired
    private InventoryRegularDao inventoryRegularDao;

    @Autowired
    private InventoryRegularDetailDao inventoryRegularDetailDao;

    @Override
    public VtuzxMap getMaterialBox(String stockNo, String locationNo, String boxNoKey) throws VtuzxException {
        List<VtuzxMap> dataList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        // 管理箱号
        String boxNo;
        // 实际箱号
        String realBoxNo;
        //批次号
//        String batchNo = "";
        boolean isBC = false;
        if (boxNoKey.contains("|;")) {
            String[] boxValues = boxNoKey.split("\\|\\;");
            if (boxValues.length == 5) {
                boxNo = boxNoKey;
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
//                batchNo = boxNo.substring(boxNo.lastIndexOf("|;") + 2);
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo + "|;batch";
                realBoxNo = boxNoKey;
                isBC = true;
            } else if (boxValues.length == 6) {
                boxNo = boxValues[boxValues.length - 1];
            } else {
                boxNo = boxNoKey;
//                realBoxNo = boxNo;
                isBC = true;
            }
        } else if (boxNoKey.startsWith("http")) { //如果是扫的追溯码
            boxNo = boxNoKey;
        } else {
            boxNo = boxNoKey;
            realBoxNo = boxNo;
        }

        Criteria getCond = new Criteria();
        if (boxNo.startsWith("T")) {
            Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(Criteria.where("trayNumber").is(boxNo).and("status").is("in").and("stockNo").is(stockNo), null, 0, 0);
            while (inventoryDetailItr.hasNext()) {
                Document inventoryDetail = inventoryDetailItr.next();
                String detailBoxNo = VtuzxUtil.getString(inventoryDetail, "boxNo");
                boxNoList.add(detailBoxNo);
            }

            for (String box : boxNoList) {
                Criteria cond = Criteria.where("boxNo").is(box).and("status").is("in");
                if (!VtuzxUtil.isEmpty(locationNo)) {
                    cond.and("locationNo").is(locationNo);
                }
                // 箱号存在校验
                Document inventoryDetail = inventoryDetailDao.findOne(cond);
                if (inventoryDetail == null) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_box_not_in_inventory, box);
                }
                // 物料存在校验
                ObjectId _inventoryId = VtuzxUtil.getObject(inventoryDetail, "_inventoryId");
                Document inventory = inventoryDao.findById(_inventoryId);
                if (inventory == null) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_box_not_in_inventory, box);
                }
                if (!stockNo.equals(VtuzxUtil.getString(inventory, "stockNo"))) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_box_not_in_inventory, box);
                }
                String materialNo = VtuzxUtil.getString(inventory, "materialNo");
                Document material = materialDao.findOne(Criteria.where("fNumber").is(materialNo));

                VtuzxMap result = new VtuzxMap("stockNo", stockNo);
                result.append("materialNo", materialNo);
                result.append("materialName", VtuzxUtil.getString(material, "fName"));
                result.append("materialSpec", VtuzxUtil.getString(material, "fSpecification"));
                result.append("batchNo", VtuzxUtil.getString(inventory, "batchNo"));
                result.append("unit", VtuzxUtil.getString(inventory, "unit"));
                result.append("produceDate", VtuzxUtil.getString(inventory, "produceDate"));
                result.append("exp", VtuzxUtil.getInt(inventory, "exp"));
                result.append("locationNo", VtuzxUtil.getString(inventoryDetail, "locationNo"));
                result.append("count", VtuzxUtil.getDouble(inventoryDetail, "count"));
                result.append("boxNo", box);
                result.append("barcodeType", isBC ? "BC" : "");
                result.append("inStockTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(inventoryDetail, "createTime")));
                dataList.add(result);
            }
        } else {
            if (boxNo.startsWith("http")) {
                String boxNoStr = boxNo.substring(boxNo.indexOf("Z"));
                getCond.orOperator(Criteria.where("traceBack.traceBackNo").is(boxNo), Criteria.where("traceBack.traceBackNo").is(boxNoStr))
                        .and("traceBack.status").is("in")
                        .and("stockNo").is(stockNo);
//                getCond.and("traceBack.traceBackNo").is(boxNo).and("traceBack.status").is("in").and("stockNo").is(stockNo);
            } else {
                getCond.and("boxNo").is(boxNo).and("status").is("in").and("stockNo").is(stockNo).and("count").gt(0);
                if (!VtuzxUtil.isEmpty(locationNo)) {
                    getCond.and("locationNo").is(locationNo);
                }
            }
            // 箱号存在校验
            Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(getCond, null, 0, 0);
            while (inventoryDetailItr.hasNext()) {
                Document inventoryDetail = inventoryDetailItr.next();
                String invBoxNo = VtuzxUtil.getString(inventoryDetail, "boxNo");
                if (boxNo.startsWith("http")) {
                    List<Document> traceBackList = VtuzxUtil.getObject(inventoryDetail, "traceBack");
                    for (Document traceBack: traceBackList) {
                        String traceBackNo = VtuzxUtil.getString(traceBack, "traceBackNo");
                        String status = VtuzxUtil.getString(traceBack, "status");
                        if (traceBackNo.equals(boxNo) && "out".equals(status)) {
                            throw new VtuzxException("追溯码不在库");
                        }
                    }
                }
                ObjectId _inventoryId = VtuzxUtil.getObject(inventoryDetail, "_inventoryId");
                Document inventory = inventoryDao.findById(_inventoryId);
                if (inventory == null) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_box_not_in_inventory, boxNo);
                }
//                String inBatchNo = VtuzxUtil.getString(inventory,"batchNo");
//                if (inBatchNo.equals(batchNo)){
//
//                }
                String materialNo = VtuzxUtil.getString(inventory, "materialNo");
                Document material = materialDao.findOne(Criteria.where("fNumber").is(materialNo));

                VtuzxMap result = new VtuzxMap("stockNo", stockNo);
                result.append("materialNo", materialNo);
                result.append("materialName", VtuzxUtil.getString(material, "fName"));
                result.append("materialSpec", VtuzxUtil.getString(material, "fSpecification"));
                result.append("batchNo", VtuzxUtil.getString(inventory, "batchNo"));
                result.append("unit", VtuzxUtil.getString(inventory, "unit"));
                result.append("produceDate", VtuzxUtil.getString(inventory, "produceDate"));
                result.append("exp", VtuzxUtil.getInt(inventory, "exp"));
                result.append("locationNo", VtuzxUtil.getString(inventoryDetail, "locationNo"));
                result.append("count", VtuzxUtil.getDouble(inventoryDetail, "count"));
                result.append("boxNo", invBoxNo);
                result.append("barcodeType", isBC ? "BC" : "");
                result.append("inStockTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(inventoryDetail, "createTime")));
                if (boxNo.startsWith("http")) {
                    result.append("traceBack", boxNo);
                    result.append("isZS", true);
                }
                dataList.add(result);
            }
        }
        if (dataList.size() == 0) {
            throw new VtuzxException("箱号不在库");
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public void addInventory(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String stockNo, String billNo, List<BillMaterial> billMaterialList) throws VtuzxException {
        _logger.info(String.format("即时库存入库开始: 事件[%s], 单据类型[%s], 单号[%s]", billEvent, billType.label(), billNo));
        int sign = billType.inventoryIO();
        if (sign == 0) {
            throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_bill_failure, billNo, billType.label(), billEvent.label());
        }
        switch (billEvent) {
            case Audit: {
                if (sign < 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            case Unaudit: {
                if (sign > 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            default: {
                throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
            }
        }
        String transactionKey = IXqcConst.getTransactionKey();
        _logger.info(String.format("即时库存入库事务启动: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        Map<ObjectId, Document> updateInventoryMap = new HashMap<>();
        try {
            for (BillMaterial material : billMaterialList) {
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
                addInventoryMaterial(_userId, userName, transactionKey, stockNo, billEvent, billType, billNo, material, updateInventoryMap);
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
            }
            _logger.info(String.format("即时库存入库事务完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        } catch (Exception e) {
            _logger.warn(String.format("即时库存入库异常: 事件[%s], 单据类型[%s], 单号[%s], %s", billEvent.label(), billType.label(), billNo, e.getMessage()), e);
            for (ObjectId _updateId : updateInventoryMap.keySet()) {
                Document incDoc = updateInventoryMap.get(_updateId);
                incDoc.append("boxCount", -1 * VtuzxUtil.getInt(incDoc, "boxCount"));
                incDoc.append("count", -1 * VtuzxUtil.getDouble(incDoc, "count"));
                inventoryDao.updateByIdRedirect(_updateId, new Document("$inc", incDoc));
            }
            Criteria rollbackCond = Criteria.where("transactionKey").is(transactionKey);
            inventoryDao.deleteMany(rollbackCond);
            inventoryHistoryDao.deleteMany(rollbackCond);
            inventoryDetailDao.deleteMany(rollbackCond);
            inventoryHistoryDetailDao.deleteMany(rollbackCond);
            _logger.info(String.format("即时库存入库回滚完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
            throw e;
        }
    }


    @Override
    public void addInventory(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String billNo, List<BillMaterial> billMaterialList) throws VtuzxException {
        _logger.info(String.format("即时库存入库开始: 事件[%s], 单据类型[%s], 单号[%s]", billEvent, billType.label(), billNo));
        int sign = billType.inventoryIO();
        if (sign == 0) {
            throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_bill_failure, billNo, billType.label(), billEvent.label());
        }
        switch (billEvent) {
            case Audit: {
                if (sign < 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            case Unaudit: {
                if (sign > 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            default: {
                throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
            }
        }
        String transactionKey = IXqcConst.getTransactionKey();
        _logger.info(String.format("即时库存入库事务启动: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        Map<ObjectId, Document> updateInventoryMap = new HashMap<>();
        try {
            for (BillMaterial material : billMaterialList) {
                String stockNo = material.getStockNo();
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
                addInventoryMaterial(_userId, userName, transactionKey, stockNo, billEvent, billType, billNo, material, updateInventoryMap);
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
            }
            _logger.info(String.format("即时库存入库事务完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        } catch (Exception e) {
            _logger.warn(String.format("即时库存入库异常: 事件[%s], 单据类型[%s], 单号[%s], %s", billEvent.label(), billType.label(), billNo, e.getMessage()), e);
            for (ObjectId _updateId : updateInventoryMap.keySet()) {
                Document incDoc = updateInventoryMap.get(_updateId);
                incDoc.append("boxCount", -1 * VtuzxUtil.getInt(incDoc, "boxCount"));
                incDoc.append("count", -1 * VtuzxUtil.getDouble(incDoc, "count"));
                inventoryDao.updateByIdRedirect(_updateId, new Document("$inc", incDoc));
            }
            Criteria rollbackCond = Criteria.where("transactionKey").is(transactionKey);
            inventoryDao.deleteMany(rollbackCond);
            inventoryHistoryDao.deleteMany(rollbackCond);
            inventoryDetailDao.deleteMany(rollbackCond);
            inventoryHistoryDetailDao.deleteMany(rollbackCond);
            _logger.info(String.format("即时库存入库回滚完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
            throw e;
        }
    }

    @Override
    public void addInventoryTran(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String billNo, List<BillMaterial> billMaterialList) throws VtuzxException {
        _logger.info(String.format("即时库存入库开始: 事件[%s], 单据类型[%s], 单号[%s]", billEvent, billType.label(), billNo));
        int sign = billType.inventoryIO();
        if (sign == 0) {
            throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_bill_failure, billNo, billType.label(), billEvent.label());
        }
        switch (billEvent) {
            case Audit: {
                if (sign < 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            case Unaudit: {
                if (sign > 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            default: {
                throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_event_failure, billNo, billType.label(), billEvent.label());
            }
        }
        String transactionKey = IXqcConst.getTransactionKey();
        _logger.info(String.format("即时库存入库事务启动: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        Map<ObjectId, Document> updateInventoryMap = new HashMap<>();
        try {
            for (BillMaterial material : billMaterialList) {
                String stockNo = material.getStockNo();
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
                addInventoryMaterialTran(_userId, userName, transactionKey, stockNo, billEvent, billType, billNo, material, updateInventoryMap);
                _logger.info(String.format("物料即时库存入库: 单号[%s], 物料[%s]", billNo, material.getMaterialNo()));
            }
            _logger.info(String.format("即时库存入库事务完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        } catch (Exception e) {
            _logger.warn(String.format("即时库存入库异常: 事件[%s], 单据类型[%s], 单号[%s], %s", billEvent.label(), billType.label(), billNo, e.getMessage()), e);
            for (ObjectId _updateId : updateInventoryMap.keySet()) {
                Document incDoc = updateInventoryMap.get(_updateId);
                incDoc.append("boxCount", -1 * VtuzxUtil.getInt(incDoc, "boxCount"));
                incDoc.append("count", -1 * VtuzxUtil.getDouble(incDoc, "count"));
                inventoryDao.updateByIdRedirect(_updateId, new Document("$inc", incDoc));
            }
            Criteria rollbackCond = Criteria.where("transactionKey").is(transactionKey);
            inventoryDao.deleteMany(rollbackCond);
            inventoryHistoryDao.deleteMany(rollbackCond);
            inventoryDetailDao.deleteMany(rollbackCond);
            inventoryHistoryDetailDao.deleteMany(rollbackCond);
            _logger.info(String.format("即时库存入库回滚完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
            throw e;
        }
    }

    /**
     * 保存库存物料
     *
     * @param _userId            用户ID
     * @param userName           用户名
     * @param stockNo            仓库编码
     * @param event              单据动作
     * @param billType           单据类型
     * @param billNo             单据号
     * @param material           物料信息
     * @param updateInventoryMap 更新对象Map, 记录并用于回滚
     */
    private void addInventoryMaterial(ObjectId _userId, String userName,
                                      String transactionKey, String stockNo, BillEvent event, BillType billType, String billNo,
                                      BillMaterial material, Map<ObjectId, Document> updateInventoryMap) {
        // 查询库存当前物料
        Criteria cond = Criteria.where("stockNo").is(stockNo)
                .and("materialNo").is(material.getMaterialNo())
                .and("produceDate").is(material.getProduceDate())
                .and("exp").is(material.getExp())
                .and("batchNo").is(material.getBatchNo());

        Document inventoryDoc = inventoryDao.findOne(cond);

        List<BillMaterialBox> boxList = material.getBoxList();
        double total = 0;
        for (BillMaterialBox box : boxList) {
            total += box.getCount();
        }
        int boxCount = boxList.size();
        List<String> baocaiBoxNoList = new ArrayList<>();
        for (BillMaterialBox box : boxList) {
            String boxNo = box.getBoxNo();
            if (boxNo.contains("|;")) {
                // 是包材
                baocaiBoxNoList.add(boxNo);
            }
        }

        Document inventoryHistory;
        ObjectId _inventoryId;
        if (inventoryDoc == null) {
            inventoryDoc = new Document("transactionKey", transactionKey)
                    .append("stockNo", stockNo)
                    .append("materialNo", material.getMaterialNo())
                    .append("produceDate", material.getProduceDate())
                    .append("exp", material.getExp())
                    .append("batchNo", material.getBatchNo())
                    .append("unit", material.getUnit())
                    .append("boxCount", boxCount)
                    .append("count", total);
            inventoryHistory = new Document(inventoryDoc).append("billNo", billNo).append("billType", billType.toString()).append("billEvent", event.toString());
            _inventoryId = inventoryDao.insertStock(_userId, userName, inventoryDoc);
        } else {
            _inventoryId = inventoryDoc.getObjectId(IVtuzxConst.Key__id);
            Document incDoc = new Document("boxCount", boxCount - baocaiBoxNoList.size()).append("count", total);
            inventoryDao.updateStock(_userId, userName, _inventoryId, incDoc);
            updateInventoryMap.put(_inventoryId, incDoc);
            inventoryHistory = new Document("transactionKey", transactionKey)
                    .append("billNo", billNo)
                    .append("stockNo", stockNo)
                    .append("materialNo", material.getMaterialNo())
                    .append("produceDate", material.getProduceDate())
                    .append("exp", material.getExp())
                    .append("batchNo", material.getBatchNo())
                    .append("unit", material.getUnit())
                    .append("boxCount", boxCount)
                    .append("count", total);
        }
        inventoryHistory.append("_inventoryId", _inventoryId);
        ObjectId _historyId = inventoryHistoryDao.insertHistory(_userId, userName, inventoryHistory);

        VtuzxMap baocaiMap = new VtuzxMap();
        if (baocaiBoxNoList.size() > 0) {
            Iterator<Document> existsBoxItr = inventoryDetailDao.findList(Criteria.where("boxNo").in(baocaiBoxNoList), null, 0, 0);
            while(existsBoxItr.hasNext()) {
                Document box = existsBoxItr.next();
                String boxNo = VtuzxUtil.getString(box, "boxNo");
                String locationNo = VtuzxUtil.getString(box, "locationNo");
                baocaiMap.append(boxNo + locationNo, box);
            }
        }

        List<Document> detailDocList = new ArrayList<>();
        List<Document> historyDetailDocList = new ArrayList<>();
        for (BillMaterialBox box : boxList) {
            String boxNo = box.getBoxNo();
            String locationNo = box.getLocationNo();
            List<Document> traceBack = box.getTraceBack();
            double count = box.getCount();
            Document detailDoc = new Document("transactionKey", transactionKey)
                    .append("boxNo", box.getBoxNo())
                    .append("rfid", box.getRfid())
                    .append("count", box.getCount())
                    .append("traceBack", box.getTraceBack())
                    .append("trayNumber", box.getTrayNumber())
                    .append("isTaskIn", box.getIsTaskIn())
                    .append("stockNo", stockNo)
                    .append("locationNo", box.getLocationNo())
                    .append("status", StockStatus.In.toString())
                    //.append("traceCount",count / traceBack.size())//追溯码重量
                    .append("_inventoryId", _inventoryId);
            if (!VtuzxUtil.isEmpty(traceBack)){
                detailDoc.append("traceCount",count / traceBack.size());
            }

            Document historyDetailDoc = new Document(detailDoc)
                    .append("billNo", billNo)
                    .append("billType", billType.toString())
                    .append("billEvent", event.toString())
                    .append("_historyId", _historyId);

            if (baocaiMap.containsKey(boxNo + locationNo)) {
                Document baocaiBox = VtuzxUtil.getObject(baocaiMap, boxNo + locationNo);
                ObjectId _baocaiId = VtuzxUtil.getObject(baocaiBox, IVtuzxConst.Key__id);
                Document incDoc = new Document();
                incDoc.append("count", BigDecimal.valueOf(box.getCount()).intValue());
                inventoryDetailDao.updateByIdRedirect(_baocaiId, new Document("$inc", incDoc));
            } else {
                detailDocList.add(detailDoc);
            }
            historyDetailDocList.add(historyDetailDoc);
        }
        if (!VtuzxUtil.isEmpty(detailDocList)) {
            inventoryDetailDao.insertDetail(_userId, userName, detailDocList);
        }
        if (!VtuzxUtil.isEmpty(historyDetailDocList)) {
            inventoryHistoryDetailDao.insertDetail(_userId, userName, historyDetailDocList);
        }
    }


    /**
     * 保存库存物料（调拨）
     *
     * @param _userId            用户ID
     * @param userName           用户名
     * @param stockNo            仓库编码
     * @param event              单据动作
     * @param billType           单据类型
     * @param billNo             单据号
     * @param material           物料信息
     * @param updateInventoryMap 更新对象Map, 记录并用于回滚
     */
    private void addInventoryMaterialTran(ObjectId _userId, String userName,
                                      String transactionKey, String stockNo, BillEvent event, BillType billType, String billNo,
                                      BillMaterial material, Map<ObjectId, Document> updateInventoryMap) throws VtuzxException {

        VtuzxMap sysParamMap = settingService.getParameters(new VtuzxMap("keys", new VtuzxArrayList<>("traceabilityCodeUrl")));
        VtuzxMap traceabilityCodeUrlMap = VtuzxUtil.getObject(sysParamMap, "traceabilityCodeUrl");
        if (traceabilityCodeUrlMap == null) {
            throw new VtuzxException(IXqcConst.Msg_E_system);
        }
        String value = VtuzxUtil.getString(traceabilityCodeUrlMap, "value");
        boolean headerIsZS = false;
        // 查询库存当前物料
        Criteria cond = Criteria.where("stockNo").is(stockNo)
                .and("materialNo").is(material.getMaterialNo())
                .and("produceDate").is(material.getProduceDate())
                .and("exp").is(material.getExp())
                .and("batchNo").is(material.getBatchNo());

        Document inventoryDoc = inventoryDao.findOne(cond);

        List<BillMaterialBox> boxList = material.getBoxList();
        double total = 0;
        for (BillMaterialBox box : boxList) {
            total += box.getCount();
        }
        int boxCount = boxList.size();
        List<String> baocaiBoxNoList = new ArrayList<>();
        List<String> zsTraceBackList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        for (BillMaterialBox box : boxList) {
            String boxNo = box.getBoxNo();
            String traceBack = box.getTraceBackNo();
            boolean isZS = box.getIsZS();
            if (boxNo.contains("|;")) {
                // 是包材
                baocaiBoxNoList.add(boxNo);
            } else {
                if (isZS) {
                    headerIsZS = true;
                    zsTraceBackList.add(traceBack);
                } else {
                    boxNoList.add(boxNo);
                }
            }
        }

        VtuzxMap boxNoMap = new VtuzxMap();
        if (headerIsZS) {
            List<Document> boxDocList = new ArrayList<>();
            if (zsTraceBackList.size() > 0) {
                Iterator<Document> boxNoItr = inventoryDetailDao.findList(Criteria.where("traceBack.traceBackNo").in(zsTraceBackList), null, 0, 0);
                while(boxNoItr.hasNext()) {
                    Document box = boxNoItr.next();
                    boxDocList.add(box);
                }
//                for (String zsTraceBack: zsTraceBackList) {
//                    Document conTwoDoc = new Document("$elemMatch", new Document("status", "in").append("traceBackNo", zsTraceBack));
//                    Document boxNoDoc = inventoryDetailDao.findOne(Criteria.where("traceBack").is(new Document("traceBack", conTwoDoc)));
//                    boxDocList.add(boxNoDoc);
//                }

                for (Document boxDoc: boxDocList) {
                    String boxNo = VtuzxUtil.getString(boxDoc, "boxNo");
                    List<Document> traceBack = VtuzxUtil.getObject(boxDoc, "traceBack");
                    for (Document traceBackDoc: traceBack) {
                        List<String> boxNoStringList = VtuzxUtil.getObject(boxNoMap, boxNo);
                        String traceBackNo = "";
                        String traceBackNoStr = VtuzxUtil.getString(traceBackDoc, "traceBackNo");
                        if (!traceBackNoStr.startsWith("http")) {
                            traceBackNo = value + "?traceabilityCode=" + traceBackNoStr;
                        } else {
                            traceBackNo = traceBackNoStr;
                        }
                        if (zsTraceBackList.contains(traceBackNo)) {
                            if (VtuzxUtil.isEmpty(boxNoStringList)) {
                                List<String> traceBackNoList = new ArrayList<>();
                                traceBackNoList.add(traceBackNo);
                                boxNoMap.append(boxNo, traceBackNoList);
                            } else {
                                boxNoStringList.add(traceBackNo);
                            }
                        }
                    }
                }
            }
        } else {
            Iterator<Document> boxNoItr = inventoryDetailDao.findList(Criteria.where("boxNo").in(boxNoList), null, 0, 0);
            while(boxNoItr.hasNext()) {
                Document box = boxNoItr.next();
                String getBoxNo = VtuzxUtil.getString(box, "boxNo");
                List<Document> traceBack = new ArrayList<>();
                try {
                    traceBack = VtuzxUtil.getObject(box, "traceBack");
                } catch (Exception e){
                    throw new VtuzxException("当前无法调拨请联系管理员");
                }
                for (BillMaterialBox box1: boxList) {
                    String boxNo = box1.getBoxNo();
                    if (getBoxNo.equals(boxNo)) {
                        box1.setTraceBack(traceBack);
                    }
                }
            }
        }


        Document inventoryHistory;
        ObjectId _inventoryId;
        if (inventoryDoc == null) {
            inventoryDoc = new Document("transactionKey", transactionKey)
                    .append("stockNo", stockNo)
                    .append("materialNo", material.getMaterialNo())
                    .append("produceDate", material.getProduceDate())
                    .append("exp", material.getExp())
                    .append("batchNo", material.getBatchNo())
                    .append("unit", material.getUnit())
                    .append("boxCount", boxCount);
            if (headerIsZS) {
                inventoryDoc.append("count", boxNoMap.size());
            } else {
                inventoryDoc.append("count", total);
            }
            inventoryHistory = new Document(inventoryDoc).append("billNo", billNo).append("billType", billType.toString()).append("billEvent", event.toString());
            _inventoryId = inventoryDao.insertStock(_userId, userName, inventoryDoc);
        } else {
            _inventoryId = inventoryDoc.getObjectId(IVtuzxConst.Key__id);
            Document incDoc = new Document(); //计算单据头数量有不确定性
            if (headerIsZS) {
                incDoc.append("count", boxNoMap.size());
                incDoc.append("boxCount", boxCount + boxNoMap.size());
            } else {
                incDoc.append("count", total);
                incDoc.append("boxCount", boxCount - baocaiBoxNoList.size());
            }
            inventoryDao.updateStock(_userId, userName, _inventoryId, incDoc);
            updateInventoryMap.put(_inventoryId, incDoc);
            inventoryHistory = new Document("transactionKey", transactionKey)
                    .append("billNo", billNo)
                    .append("stockNo", stockNo)
                    .append("materialNo", material.getMaterialNo())
                    .append("produceDate", material.getProduceDate())
                    .append("exp", material.getExp())
                    .append("batchNo", material.getBatchNo())
                    .append("unit", material.getUnit())
                    .append("boxCount", boxCount)
                    .append("count", total);
        }
        inventoryHistory.append("_inventoryId", _inventoryId);
        ObjectId _historyId = inventoryHistoryDao.insertHistory(_userId, userName, inventoryHistory);

        VtuzxMap baocaiMap = new VtuzxMap();
        if (baocaiBoxNoList.size() > 0) {
            Iterator<Document> existsBoxItr = inventoryDetailDao.findList(Criteria.where("boxNo").in(baocaiBoxNoList), null, 0, 0);
            while(existsBoxItr.hasNext()) {
                Document box = existsBoxItr.next();
                String boxNo = VtuzxUtil.getString(box, "boxNo");
                String locationNo = VtuzxUtil.getString(box, "locationNo");
                baocaiMap.append(boxNo + locationNo, box);
            }
        }

        List<Document> detailDocList = new ArrayList<>();
        List<Document> historyDetailDocList = new ArrayList<>();
        for (BillMaterialBox box : boxList) {
            String boxNo = box.getBoxNo();
            String locationNo = box.getLocationNo();
            String traceBackNo = "";
            List<Document> traceBackMapList = new ArrayList<>();
            if (headerIsZS) {
                traceBackNo = box.getTraceBackNo();
            } else {
                traceBackMapList = box.getTraceBack();
            }

            Document detailDoc = new Document("transactionKey", transactionKey)
                    .append("rfid", box.getRfid())
                    .append("count", box.getCount())
                    .append("trayNumber", box.getTrayNumber())
                    .append("isTaskIn", box.getIsTaskIn())
                    .append("stockNo", stockNo)
                    .append("locationNo", box.getLocationNo())
                    .append("status", StockStatus.In.toString())
                    .append("_inventoryId", _inventoryId);
            if (!VtuzxUtil.isEmpty(traceBackNo)) {
                List<Document> traceBack = new ArrayList<>();
                Document document = new Document("traceBackNo", traceBackNo).append("status", "in");
                traceBack.add(document);
                detailDoc.append("traceBack", traceBack);
                detailDoc.append("oldBoxNo", box.getBoxNo());
            } else if (!VtuzxUtil.isEmpty(traceBackMapList)) {
                List<Document> list = new ArrayList<>();
                for (Document traceBackMap: traceBackMapList) {
                    String status = VtuzxUtil.getString(traceBackMap, "status");
                    if ("in".equals(status)) {
                        list.add(traceBackMap);
                    }
                }
                detailDoc.append("traceBack", list);
                detailDoc.append("boxNo", box.getBoxNo());
            } else {
                detailDoc.append("boxNo", box.getBoxNo());
            }


            Document historyDetailDoc = new Document(detailDoc)
                    .append("billNo", billNo)
                    .append("billType", billType.toString())
                    .append("billEvent", event.toString())
                    .append("_historyId", _historyId);

            if (baocaiMap.containsKey(boxNo + locationNo)) {
                Document baocaiBox = VtuzxUtil.getObject(baocaiMap, boxNo + locationNo);
                ObjectId _baocaiId = VtuzxUtil.getObject(baocaiBox, IVtuzxConst.Key__id);
                Document incDoc = new Document();
                incDoc.append("count", BigDecimal.valueOf(box.getCount()).intValue());
                inventoryDetailDao.updateByIdRedirect(_baocaiId, new Document("$inc", incDoc));
            } else {
                detailDocList.add(detailDoc);
            }
            historyDetailDocList.add(historyDetailDoc);
        }

        List<String> oldBoxNoList = new ArrayList<>(); //已经处理过的箱号
        if (!VtuzxUtil.isEmpty(boxNoMap)) {
            for(Document detailDoc: detailDocList) { //已经存储好的数据
                List<Document> traceBackDoc = new ArrayList<>();
                String oldBoxNo = VtuzxUtil.getString(detailDoc, "oldBoxNo");
                if (!oldBoxNoList.contains(oldBoxNo)) {
                    boxNoMap.forEach((k, v) -> { //需要整理的数据
                        List<String> traceBackList = (List<String>) v;
                        if (oldBoxNo.equals(k)) {
                            oldBoxNoList.add(oldBoxNo);
                            for (String traceBack : traceBackList) {
                                Document document = new Document("traceBackNo", traceBack).append("status", "in");
                                traceBackDoc.add(document);
                                detailDoc.append("traceBack", traceBackDoc);
                                detailDoc.append("isQualified", true);
                                detailDoc.append("count", traceBackList.size());
                            }

                        }
                    });
                }
            }
        }
        if (headerIsZS) {
            List<Document> addDetailList = new ArrayList<>();
            if (oldBoxNoList.size() != 0) {
                for (Document detailDoc: detailDocList) {
                    boolean isQualified = VtuzxUtil.getBoolean(detailDoc, "isQualified");
                    if (isQualified) {
                        String oldBoxNo = VtuzxUtil.getString(detailDoc, "oldBoxNo");
                        detailDoc.append("boxNo", oldBoxNo.substring(0, 3) + new Date().getTime());
                        addDetailList.add(detailDoc);
                    }
                }
            }
            detailDocList = new ArrayList<>(addDetailList);
        }




        if (!VtuzxUtil.isEmpty(detailDocList)) {
            inventoryDetailDao.insertDetail(_userId, userName, detailDocList);
        }
        if (!VtuzxUtil.isEmpty(historyDetailDocList)) {
            inventoryHistoryDetailDao.insertDetail(_userId, userName, historyDetailDocList);
        }
    }


    @Override
    public void reduceInventory(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String stockNo, String billNo, List<String> boxNoList, List<VtuzxMap> bcBoxList) throws VtuzxException {
        coreInventoryService.reduceInventory(_userId, userName, billEvent, billType, stockNo, billNo, boxNoList, bcBoxList);
    }

    @Override
    public void reduceInventory(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String stockNo, String billNo, List<String> boxNoList, List<VtuzxMap> bcBoxList, List<Document> zsBoxList) throws VtuzxException {
        coreInventoryService.reduceInventory(_userId, userName, billEvent, billType, stockNo, billNo, boxNoList, bcBoxList, zsBoxList);
//        int sign = billType.inventoryIO();
//        if (sign == 0) {
//            throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_bill_failure, billNo, billType.label(), billEvent.label());
//        }
//        switch (billEvent) {
//            case Audit: {
//                if (sign > 0) {
//                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
//                }
//                break;
//            }
//            case Unaudit: {
//                if (sign < 0) {
//                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
//                }
//                break;
//            }
//            default: {
//                throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
//            }
//        }
//        String transactionKey = IXqcConst.getTransactionKey();
//        _logger.info(String.format("库存增加事务启动: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
//        Map<ObjectId, Document> updateInventoryMap = new HashMap<>();
//        Map<ObjectId, Document> updatedInventoryMap = new HashMap<>();
//        try {
//            if (!VtuzxUtil.isEmpty(boxNoList)) {
//                Criteria updCond = Criteria.where("boxNo").in(boxNoList).and("stockNo").is(stockNo).and("status").is(StockStatus.In.toString());
//                Iterator<Document> detailItr = inventoryDetailDao.findList(updCond, null, 0, 0);
//                while (detailItr.hasNext()) {
//                    Document detail = detailItr.next();
//                    ObjectId _inventoryId = detail.getObjectId("_inventoryId");
//                    ObjectId _id = detail.getObjectId("_id");
//                    double count = VtuzxUtil.getDouble(detail, "count");
//                    boolean isTaskIn = VtuzxUtil.getBoolean(detail, "isTaskIn");
//                    List<Document> traceBackDocList = new ArrayList<>();
//                    if (isTaskIn) {
//                        List<Document> traceBack = VtuzxUtil.getObject(detail, "traceBack");
//                        List<String> traceBackNoList = new ArrayList<>();
//                        if (!VtuzxUtil.isEmpty(traceBack)) {
//                            for (Document traceMap: traceBack) {
//                                String traceBackNo = VtuzxUtil.getString(traceMap, "traceBackNo");
//                                String status = VtuzxUtil.getString(traceMap, "status");
//                                if ("in".equals(status)) {
//                                    traceBackNoList.add(traceBackNo);
//                                    Document data = new Document("traceBackNo", traceBackNo).append("status", "out");
//                                    traceBackDocList.add(data);
//                                } else {
//                                    traceBackDocList.add(traceMap);
//                                }
//                            }
//                        }
//                        detail.append("traceBack", traceBackNoList);
//                    }
//                    Document inventory = updateInventoryMap.get(_inventoryId);
//                    if (inventory == null) {
//                        inventory = new Document("boxCount", -1).append("count", -count).append("boxList", new ArrayList<>());
//                        updateInventoryMap.put(_inventoryId, inventory);
//                    } else {
//                        inventory.append("boxCount", VtuzxUtil.getInt(inventory, "boxCount") - 1).append("count", VtuzxUtil.getDouble(inventory, "count") - count);
//                    }
//                    List<Document> boxList = VtuzxUtil.getObject(inventory, "boxList");
//                    boxList.add(detail);
//
//                    Document updateDoc = new Document("status", StockStatus.Out.toString());
//                    if (isTaskIn) {
//                        updateDoc.append("traceBack", traceBackDocList);
//                    }
//                    inventoryDetailDao.updateById(_id, updateDoc);
//                }
//            }
//            if (!VtuzxUtil.isEmpty(bcBoxList)) {
////                Criteria updCond = Criteria.where("boxNo").in(boxNoList).and("stockNo").is(stockNo).and("status").is(StockStatus.In.toString());
////                UpdateResult updRes = inventoryDetailDao.updateMany(updCond, new Document("status", StockStatus.Out.toString()));
////                if (updRes.getModifiedCount() != boxNoList.size()) {
////                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_exist);
////                }
//                List<String> bcBoxNoList = new ArrayList<>();
//                List<String> traceBackList = new ArrayList<>();
//                List<String> bcLocationNoList = new ArrayList<>();
//                VtuzxMap bcBoxNoMap = new VtuzxMap();
//                for (VtuzxMap bcBox : bcBoxList) {
//                    String boxNo = VtuzxUtil.getString(bcBox, "boxNo");
//                    String locationNo = VtuzxUtil.getString(bcBox, "locationNo");
//                    int count = VtuzxUtil.getInt(bcBox, "count");
//                    List<String> traceBack = VtuzxUtil.getObject(bcBox, "traceBack");
//                    if (!VtuzxUtil.isEmpty(traceBack)) {
//                        traceBackList.addAll(traceBack);
//                    }
//                    bcBoxNoList.add(boxNo);
//                    bcLocationNoList.add(locationNo);
//                    bcBoxNoMap.append(boxNo, count);
//                }
//                Criteria cond = new Criteria("boxNo").in(bcBoxNoList).and("stockNo").is(stockNo).and("locationNo").in(bcLocationNoList);
//                if (traceBackList.size() != 0) {
//                    cond.and("traceBack.traceBackNo").in(traceBackList);
//                }
//                Iterator<Document> detailItr = inventoryDetailDao.findList(cond, null, 0, 0);
//                while (detailItr.hasNext()) {
//                    Document detail = detailItr.next();
//                    ObjectId _id = VtuzxUtil.getObject(detail, IVtuzxConst.Key__id);
//                    ObjectId _inventoryId = detail.getObjectId("_inventoryId");
//                    List<Document> traceBackMapList = new ArrayList<>();
//                    List<String> traceBackNoList = new ArrayList<>();
//                    if (traceBackList.size() != 0) {
//                        List<Document> traceBack = VtuzxUtil.getObject(detail, "traceBack");
//                        for (Document traceMap: traceBack) {
//                            String traceBackNo = VtuzxUtil.getString(traceMap, "traceBackNo");
//                            if (traceBackList.contains(traceBackNo)) {
//                                Document data = new Document("traceBackNo", traceBackNo).append("status", "out");
//                                traceBackNoList.add(traceBackNo);
//                                traceBackMapList.add(data);
//                            } else {
//                                traceBackMapList.add(traceMap);
//                            }
//                        }
//                        detail.append("traceBack", traceBackNoList);
//                    }
//                    int count = VtuzxUtil.getInt(bcBoxNoMap, VtuzxUtil.getString(detail, "boxNo"));
//                    Document inventory = updateInventoryMap.get(_inventoryId);
//                    if (inventory == null) {
//                        inventory = new Document("count", -count).append("boxList", new ArrayList<>());
//                        updateInventoryMap.put(_inventoryId, inventory);
//                    } else {
//                        inventory.append("count", VtuzxUtil.getDouble(inventory, "count") - count);
//                    }
//                    List<Document> boxList = VtuzxUtil.getObject(inventory, "boxList");
//                    boxList.add(detail);
//                    Document decDoc = new Document();
//                    decDoc.append("count", -1 * count);
//                    Document updateDoc = new Document("$inc", decDoc);
//                    if (traceBackList.size() != 0) {
//                        updateDoc.append("$set", new Document("traceBack", traceBackMapList));
//                    }
//                    inventoryDetailDao.updateByIdRedirect(_id, updateDoc);
//                }
//            }
//            if (!VtuzxUtil.isEmpty(zsBoxList)) {
//                VtuzxMap sysParamMap = settingService.getParameters(new VtuzxMap("keys", new VtuzxArrayList<>("traceabilityCodeUrl")));
//                VtuzxMap traceabilityCodeUrlMap = VtuzxUtil.getObject(sysParamMap, "traceabilityCodeUrl");
//                if (traceabilityCodeUrlMap == null) {
//                    throw new VtuzxException(IXqcConst.Msg_E_system);
//                }
//                String value = VtuzxUtil.getString(traceabilityCodeUrlMap, "value");
//                List<String> zsBoxStrList = new ArrayList<>();
//                List<String> zsBoxHttpList = new ArrayList<>();
//                VtuzxMap arrZsBoxMap = new VtuzxMap();
//                for (Document zsBoxDoc: zsBoxList) {
//                    String traceBack = VtuzxUtil.getString(zsBoxDoc, "traceBack");
//                    String boxNo = VtuzxUtil.getString(zsBoxDoc, "boxNo");
//                    List<String> traceBackList = VtuzxUtil.getObject(arrZsBoxMap, boxNo);
//                    if (VtuzxUtil.isEmpty(traceBackList)) {
//                        List<String> arrList = new ArrayList<>();
//                        arrList.add(traceBack);
//                        arrZsBoxMap.append(boxNo, arrList);
//                    } else {
//                        traceBackList.add(traceBack);
//                    }
//                    zsBoxHttpList.add(traceBack);
//                    zsBoxStrList.add(traceBack.substring(traceBack.indexOf("Z")));
//                }
//                List<Document> invDetailDocList = new ArrayList<>();
//                Criteria cond = new Criteria();
//                cond.orOperator(Criteria.where("traceBack.traceBackNo").in(zsBoxHttpList), Criteria.where("traceBack.traceBackNo").in(zsBoxStrList)).and("status").is("in");
//                Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(cond, null, 0, 0);
//                while (inventoryDetailItr.hasNext()) {
//                    Document inventoryDetailDoc = inventoryDetailItr.next();
//                    invDetailDocList.add(inventoryDetailDoc);
//                }
//
//
//                arrZsBoxMap.forEach((k,v) -> {
//                    int invSize = 0;
//                    String id = "";
//                    String inventoryId = "";
//                    List<String> arrZsBoxList = (List<String>) v;
//                    List<Document> updateTraceBackList = new ArrayList<>();
//                    for (Document invDetailDoc: invDetailDocList) {
//                        String boxNo = VtuzxUtil.getString(invDetailDoc, "boxNo");
//                        List<Document> traceBack = VtuzxUtil.getObject(invDetailDoc, "traceBack");
//                        if (k.equals(boxNo)) {
//                            id = VtuzxUtil.getString(invDetailDoc, "_id");
//                            inventoryId = VtuzxUtil.getString(invDetailDoc, "_inventoryId");
//                            for (Document traceBackDoc: traceBack) {
//                                String traceBackNo = VtuzxUtil.getString(traceBackDoc, "traceBackNo");
//                                String traceBackNoStr = "";
//                                if (traceBackNo.startsWith("Z")) {
//                                    traceBackNoStr = value + "?traceabilityCode=" + traceBackNo;
//                                } else {
//                                    traceBackNoStr = traceBackNo;
//                                }
//                                String status = VtuzxUtil.getString(traceBackDoc, "status");
//                                Document updateTraceBackDoc = new Document("traceBackNo", traceBackNo);
//                                if (arrZsBoxList.contains(traceBackNoStr)) { //更新
//                                    updateTraceBackDoc.append("status", "out");
//                                } else {
//                                    updateTraceBackDoc.append("status", status);
//                                }
//                                if ("in".equals(VtuzxUtil.getString(updateTraceBackDoc ,"status"))) {
//                                    invSize++;
//                                }
//                                updateTraceBackList.add(updateTraceBackDoc);
//                            }
//                        }
//                    }
//                    if (invSize >= 1) { //invSize = 1 是还有在库的
//                        inventoryDetailDao.updateByIdRedirect(id, new Document("$inc", new Document("count", -arrZsBoxList.size())).append("$set", new Document("traceBack", updateTraceBackList)));
//                        inventoryDao.updateByIdRedirect(inventoryId, new Document("$inc", new Document("count", -arrZsBoxList.size())));
//                    } else {
//                        inventoryDetailDao.updateByIdRedirect(id, new Document("$inc", new Document("count", -arrZsBoxList.size())).append("$set", new Document("status", "out").append("traceBack", updateTraceBackList)));
//                        inventoryDao.updateByIdRedirect(inventoryId, new Document("$inc", new Document("count", -arrZsBoxList.size()).append("boxCount", -1)));
//                    }
//                });
//            }
//            for (ObjectId _inventoryId : updateInventoryMap.keySet()) {
//                reduceInventoryMaterial(_userId, userName, transactionKey, stockNo, billEvent, billType, billNo, _inventoryId, updateInventoryMap.get(_inventoryId), updatedInventoryMap);
//            }
//            _logger.info(String.format("库存增加事务完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
//        } catch (Exception e) {
//            _logger.warn(String.format("库存增加异常: 事件[%s], 单据类型[%s], 单号[%s], %s", billEvent.label(), billType.label(), billNo, e.getMessage()), e);
//            if (!VtuzxUtil.isEmpty(boxNoList)) {
//                Criteria updCond = Criteria.where("boxNo").in(boxNoList).and("stockNo").is(stockNo);
//                inventoryDetailDao.updateMany(updCond, new Document("status", StockStatus.In.toString()));
//            }
//            Criteria rollbackCond = Criteria.where("transactionKey").is(transactionKey);
//            inventoryHistoryDao.deleteMany(rollbackCond);
//            inventoryHistoryDetailDao.deleteMany(rollbackCond);
//            for (ObjectId _updateId : updatedInventoryMap.keySet()) {
//                Document incDoc = updatedInventoryMap.get(_updateId);
//                incDoc.append("boxCount", -1 * VtuzxUtil.getInt(incDoc, "boxCount"));
//                incDoc.append("count", -1 * VtuzxUtil.getDouble(incDoc, "count"));
//                inventoryDao.updateByIdRedirect(_updateId, new Document("$inc", incDoc));
//                Document decDoc = new Document();
//                decDoc.append("count", VtuzxUtil.getDouble(incDoc, "count"));
//                inventoryDetailDao.updateOneRedirect(Criteria.where("_inventoryId").is(_updateId), new Document("$inc", decDoc));
//            }
//            _logger.info(String.format("库存增加回滚完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
//            throw e;
//        }
    }


    @Override
    public void reduceInventory(ObjectId _userId, String userName, BillEvent billEvent, BillType billType, String billNo, List<String> boxNoList, List<VtuzxMap> bcBoxList) throws VtuzxException {
        _logger.info(String.format("即时库存出库开始: 事件[%s], 单据类型[%s], 单号[%s]", billEvent, billType.label(), billNo));
        int sign = billType.inventoryIO();
        if (sign == 0) {
            throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_bill_failure, billNo, billType.label(), billEvent.label());
        }
        switch (billEvent) {
            case Audit: {
                if (sign > 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            case Unaudit: {
                if (sign < 0) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
                }
                break;
            }
            default: {
                throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_event_failure, billNo, billType.label(), billEvent.label());
            }
        }
        List<String> boxNumberList = new ArrayList<>();
        for (String boxNoKey: boxNoList) {
            String boxNo = "";
            if (boxNoKey.contains("|;")) {
                String[] boxValues = boxNoKey.split("\\|\\;");
                if (boxValues.length == 5) {
                    boxNo = boxNoKey;
                    boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                    boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                    boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                    boxNo = boxNo + "|;batch";
                } else {
                    boxNo = boxValues[boxValues.length - 1];
                }
            } else {
                boxNo = boxNoKey;
            }
            boxNumberList.add(boxNo);
        }
        String transactionKey = IXqcConst.getTransactionKey();
        _logger.info(String.format("库存增加事务启动: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        Map<ObjectId, Document> updateInventoryMap = new HashMap<>();
        Map<ObjectId, Document> updatedInventoryMap = new HashMap<>();
        try {
            if (!VtuzxUtil.isEmpty(boxNumberList)) {
                Criteria updCond = Criteria.where("boxNo").in(boxNumberList).and("status").is(StockStatus.In.toString());
                Iterator<Document> detailItr = inventoryDetailDao.findList(updCond, null, 0, 0);
                UpdateResult updRes = inventoryDetailDao.updateMany(updCond, new Document("status", StockStatus.Out.toString()));
                if (updRes.getModifiedCount() != boxNumberList.size()) {
                    throw new VtuzxException(IXqcConst.Msg_E_XQC_stock_out_exist);
                }
                while (detailItr.hasNext()) {
                    Document detail = detailItr.next();
                    ObjectId _inventoryId = detail.getObjectId("_inventoryId");
                    double count = VtuzxUtil.getDouble(detail, "count");
                    String stockNo = VtuzxUtil.getString(detail, "stockNo");
                    Document inventory = updateInventoryMap.get(_inventoryId);
                    if (inventory == null) {
                        inventory = new Document("boxCount", -1).append("count", -count).append("stockNo", stockNo).append("boxList", new ArrayList<>());
                        updateInventoryMap.put(_inventoryId, inventory);
                    } else {
                        inventory.append("boxCount", VtuzxUtil.getInt(inventory, "boxCount") - 1).append("count", VtuzxUtil.getDouble(inventory, "count") - count);
                    }
                    List<Document> boxList = VtuzxUtil.getObject(inventory, "boxList");
                    boxList.add(detail);
                }
            }
            if (!VtuzxUtil.isEmpty(bcBoxList)) {
                List<String> bcBoxNoList = new ArrayList<>();
                List<String> bcLocationNoList = new ArrayList<>();
                VtuzxMap bcBoxNoMap = new VtuzxMap();
                for (VtuzxMap bcBox : bcBoxList) {
                    String boxNo = VtuzxUtil.getString(bcBox, "boxNo");
                    String locationNo = VtuzxUtil.getString(bcBox, "locationNo");
                    int count = VtuzxUtil.getInt(bcBox, "count");
                    bcBoxNoList.add(boxNo);
                    bcLocationNoList.add(locationNo);
                    bcBoxNoMap.append(boxNo, count);
                }
                Iterator<Document> detailItr = inventoryDetailDao.findList(Criteria.where("boxNo").in(bcBoxNoList).and("locationNo").in(bcLocationNoList), null, 0, 0);
                while (detailItr.hasNext()) {
                    Document detail = detailItr.next();
                    ObjectId _id = VtuzxUtil.getObject(detail, IVtuzxConst.Key__id);
                    ObjectId _inventoryId = detail.getObjectId("_inventoryId");
                    int count = VtuzxUtil.getInt(bcBoxNoMap, VtuzxUtil.getString(detail, "boxNo"));
                    Document inventory = updateInventoryMap.get(_inventoryId);
                    if (inventory == null) {
                        inventory = new Document("count", -count).append("boxList", new ArrayList<>());
                        updateInventoryMap.put(_inventoryId, inventory);
                    } else {
                        inventory.append("count", VtuzxUtil.getDouble(inventory, "count") - count);
                    }
                    List<Document> boxList = VtuzxUtil.getObject(inventory, "boxList");
                    boxList.add(detail);
                    Document decDoc = new Document();
                    decDoc.append("count", -1 * count);
                    inventoryDetailDao.updateByIdRedirect(_id, new Document("$inc", decDoc));
                }
            }
            for (ObjectId _inventoryId : updateInventoryMap.keySet()) {
                Document updateInventory = updateInventoryMap.get(_inventoryId);
                String stockNo = VtuzxUtil.removeString(updateInventory, "stockNo");
                reduceInventoryMaterial(_userId, userName, transactionKey, stockNo, billEvent, billType, billNo, _inventoryId, updateInventory, updatedInventoryMap);
            }
            _logger.info(String.format("库存增加事务完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
        } catch (Exception e) {
            _logger.warn(String.format("库存增加异常: 事件[%s], 单据类型[%s], 单号[%s], %s", billEvent.label(), billType.label(), billNo, e.getMessage()), e);
            if (!VtuzxUtil.isEmpty(boxNoList)) {
                Criteria updCond = Criteria.where("boxNo").in(boxNoList);
                inventoryDetailDao.updateMany(updCond, new Document("status", StockStatus.In.toString()));
            }
            Criteria rollbackCond = Criteria.where("transactionKey").is(transactionKey);
            inventoryHistoryDao.deleteMany(rollbackCond);
            inventoryHistoryDetailDao.deleteMany(rollbackCond);
            for (ObjectId _updateId : updatedInventoryMap.keySet()) {
                Document incDoc = updatedInventoryMap.get(_updateId);
                incDoc.append("boxCount", -1 * VtuzxUtil.getInt(incDoc, "boxCount"));
                incDoc.append("count", -1 * VtuzxUtil.getDouble(incDoc, "count"));
                inventoryDao.updateByIdRedirect(_updateId, new Document("$inc", incDoc));
                Document decDoc = new Document();
                decDoc.append("count", VtuzxUtil.getDouble(incDoc, "count"));
                inventoryDetailDao.updateOneRedirect(Criteria.where("_inventoryId").is(_updateId), new Document("$inc", decDoc));
            }
            _logger.info(String.format("库存增加回滚完成: 事件[%s], 单据类型[%s], 单号[%s]", billEvent.label(), billType.label(), billNo));
            throw e;
        }
    }

    @Override
    public VtuzxMap getInventory(VtuzxToken token, Map<String, Object> param) {
        String boxNo = VtuzxUtil.getString(param, "boxNo");
        String heatBoxNo = VtuzxUtil.getString(param, "heatBoxNo");
        if (!VtuzxUtil.isEmpty(heatBoxNo) || !VtuzxUtil.isEmpty(boxNo)) {
            Criteria cond = new Criteria("status").is("in");
            if (!VtuzxUtil.isEmpty(heatBoxNo)) {
                cond.and("heatBoxNo").regex(heatBoxNo);
            } else if (!VtuzxUtil.isEmpty(boxNo)) {
                cond.and("boxNo").regex(boxNo);
            }
            Document inventoryMsg = inventoryDetailDao.findOne(cond);
            if (!VtuzxUtil.isEmpty(inventoryMsg)) {
                String _inventoryId = VtuzxUtil.getString(inventoryMsg, "_inventoryId");
                Document inventoryDoc = inventoryDao.findOne(Criteria.where("_id").is(new ObjectId(_inventoryId)));
                List<VtuzxMap> dataList = new ArrayList<>();
                String materialNum = VtuzxUtil.getString(inventoryDoc, "materialNo");
                Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNum));
                String materialName = VtuzxUtil.getString(materialDoc, "fName");
                String materialSpec = VtuzxUtil.getString(materialDoc, "fSpecification");
                String unit = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
                double count = VtuzxUtil.getDouble(inventoryDoc, "count");
                int boxCount = VtuzxUtil.getInt(inventoryDoc, "boxCount");
                String id = VtuzxUtil.getString(inventoryDoc, "_id");
                inventoryDoc.append("materialName", materialName);
                inventoryDoc.append("materialSpec", materialSpec);
                inventoryDoc.append("unit", unit);
                inventoryDoc.append("id", id);
                inventoryDoc.remove("_id");
                dataList.add(new VtuzxMap(inventoryDoc));
                return new VtuzxMap(IVtuzxConst.Key_total, 1).append(IVtuzxConst.Key_data, dataList).append("totalBoxCount", boxCount).append("totalCount", count);
            }
            return null;
        }
        int pageIndex = VtuzxUtil.getInt(param, "pageIndex");
        int pageSize = VtuzxUtil.getInt(param, "pageSize");
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String materialNo = VtuzxUtil.getString(param, "materialNo");
        String batchMaterialNo = VtuzxUtil.getString(param, "batchMaterialNo");
        String fOraCKYTSX = VtuzxUtil.getString(param, "fOraCKYTSX");
        List<String> stockList = new ArrayList<>();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fOraCKYTSX").is(fOraCKYTSX), null, 0, 0);
        while (stockItr.hasNext()) {
            Document stockDoc = stockItr.next();
            String stockNumber = VtuzxUtil.getString(stockDoc, "fNumber");
            stockList.add(stockNumber);
        }
        Document material = materialDao.findOne(Criteria.where("fNumber").is(batchMaterialNo));
        List<String> materialList = new ArrayList<>();
        if (!VtuzxUtil.isEmpty(material)) {
            String materialName = VtuzxUtil.getString(material, "fName");
            Iterator<Document> materialItr = materialDao.findList(Criteria.where("fName").is(materialName), null, 0, 0);
            VtuzxMap materialMap = FastUtil.itr2Map(materialItr, "fNumber");
            materialMap.forEach((key, value) -> {
                materialList.add(key);
            });
        }
        String batchNo = VtuzxUtil.getString(param, "batchNo");
        List<String> produceDate = VtuzxUtil.getObject(param, "produceDate");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(produceDate)) {
            cond.and("produceDate").gte(VtuzxUtil.parseDate(produceDate.get(0))).lte(VtuzxUtil.parseDate(produceDate.get(1)));
        }
        if (!VtuzxUtil.isEmpty(batchNo)) {
            if(batchNo.startsWith("*")){
                batchNo = "\\" + batchNo;
            }
            cond.and("batchNo").regex(batchNo,"i");
        }
        if (!VtuzxUtil.isEmpty(materialList) || !VtuzxUtil.isEmpty(materialNo)) {
            if (!VtuzxUtil.isEmpty(materialList) && !VtuzxUtil.isEmpty(materialNo)) {
                materialList.add(materialNo);
                cond.and("materialNo").in(materialList);
            } else if (!VtuzxUtil.isEmpty(materialList)) {
                cond.and("materialNo").in(materialList);
            } else if (!VtuzxUtil.isEmpty(materialNo)) {
                cond.and("materialNo").is(materialNo);
            }
        }
        if (!VtuzxUtil.isEmpty(stockNo)) {
            if (!VtuzxUtil.isEmpty(stockList) && !stockList.contains(stockNo)) {
                // 结果是0，直接返回
                return new VtuzxMap(IVtuzxConst.Key_total, 0).append("totalBoxCount", 0).append("totalCount", 0);
            }
            cond.and("stockNo").is(stockNo);
        } else if (!VtuzxUtil.isEmpty(stockList)) {
            cond.and("stockNo").in(stockList);
        }
        List<String> materialNoList = new ArrayList<>();
        Document doc = inventoryDao.statStock(cond);
        int totalBoxCount = 0;
        double totalCount = 0;
        if (doc != null) {
            totalBoxCount = VtuzxUtil.getInt(doc, "totalBoxCount");
            totalCount = VtuzxUtil.getDouble(doc, "totalCount");
        }

        VtuzxPage page = inventoryDao.page(cond.and("boxCount").gt(0).and("count").gt(0), new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(itr -> {
            VtuzxMap data = new VtuzxMap(itr);
            String materialNumber = VtuzxUtil.getString(data, "materialNo");
            if (!materialNoList.contains(materialNumber)) {
                materialNoList.add(materialNumber);
            }
            data.append("id", VtuzxUtil.removeString(data, IVtuzxConst.Key__id));
            data.append(IVtuzxConst.Key_createTime, VtuzxUtil.formatYmdHmF(VtuzxUtil.removeObject(data, IVtuzxConst.Key_createTime)));
            data.remove(IVtuzxConst.Key_updateTime);
            data.remove("_createUserId");
            data.remove("_updateUserId");
            return data;
        });
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList), null, 0, 0);
        VtuzxMap materialMap = FastUtil.itr2Map(materialItr, "fNumber");
        for(VtuzxMap data: dataList) {
            String materialNumber = VtuzxUtil.getString(data, "materialNo");
            Document materialDoc = VtuzxUtil.getObject(materialMap, materialNumber);
            if (materialDoc != null) {
                String materialName = VtuzxUtil.getString(materialDoc, "fName");
                String fSpecification = VtuzxUtil.getString(materialDoc, "fSpecification");
                String fBaseUnitIdFName = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
                data.append("materialName", materialName);
                data.append("materialSpec", fSpecification);
                data.append("unit", fBaseUnitIdFName);
            }

        }
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList).append("totalBoxCount", totalBoxCount).append("totalCount", totalCount);
    }

    @Override
    public VtuzxDownloadBean downloadInventory(VtuzxToken token, Map<String, Object> param) throws Exception {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String materialNo = VtuzxUtil.getString(param, "materialNo");
        String batchNo = VtuzxUtil.getString(param, "batchNo");
        String boxNo = VtuzxUtil.getString(param, "boxNo");
        String fOraCKYTSX = VtuzxUtil.getString(param, "fOraCKYTSX");
        List<String> produceDate = VtuzxUtil.getObject(param, "produceDate");
        //添加条件下载否则无法查询
        if(VtuzxUtil.isEmpty(stockNo) && VtuzxUtil.isEmpty(materialNo) && VtuzxUtil.isEmpty(batchNo)){
            throw new VtuzxException("数据过多，建议按照仓库导出");
        }
        Criteria cond = new Criteria("boxCount").gt(0);
        if (!VtuzxUtil.isEmpty(produceDate)) {
            cond.and("produceDate").gte(VtuzxUtil.parseDate(produceDate.get(0))).lte(VtuzxUtil.parseDate(produceDate.get(1)));
        }
        if (!VtuzxUtil.isEmpty(batchNo)) {
            cond.and("batchNo").regex("^" + batchNo + ".*$");
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            cond.and("materialNo").regex("^" + materialNo + ".*$");
        }
        if (!VtuzxUtil.isEmpty(fOraCKYTSX) || !VtuzxUtil.isEmpty(stockNo)) {
            List<String> stockNumList = new ArrayList<>();
            if ((!VtuzxUtil.isEmpty(fOraCKYTSX) && !VtuzxUtil.isEmpty(stockNo)) || !VtuzxUtil.isEmpty(fOraCKYTSX)) {
                Iterator<Document> stockItr = stockDao.findList(Criteria.where("fOraCKYTSX").is(fOraCKYTSX), null, 0, 0);
                while (stockItr.hasNext()) {
                    Document stockDoc = stockItr.next();
                    String stockNum = VtuzxUtil.getString(stockDoc, "fNumber");
                    stockNumList.add(stockNum);
                }
                if (!VtuzxUtil.isEmpty(fOraCKYTSX) && !VtuzxUtil.isEmpty(stockNo)) {
                    if (!stockNumList.contains(stockNo)) {
                        stockNumList.add(stockNo);
                    }
                }
                cond.and("stockNo").in(stockNumList);
            } else if (!VtuzxUtil.isEmpty(stockNo)) {
                cond.and("stockNo").is(stockNo);
            }
        }

        Iterator<Document>  inventoryItr = inventoryDao.findList(cond, new Document("createTime", 1), 0, 0);
        List<ObjectId> ids = new ArrayList<>();
        List<String> materialList = new ArrayList<>();
        List<String> stockList = new ArrayList<>();
        VtuzxMap data = new VtuzxMap();
        while (inventoryItr.hasNext()) {
            Document statDoc = inventoryItr.next();
            String id = VtuzxUtil.getString(statDoc, "_id");
            String materialNumber = VtuzxUtil.getString(statDoc, "materialNo");
            String stockNum = VtuzxUtil.getString(statDoc, "stockNo");
            String batchNum = VtuzxUtil.getString(statDoc, "batchNo");
            int boxCount = VtuzxUtil.getInt(statDoc, "boxCount");
            Double count = VtuzxUtil.getDouble(statDoc, "count");
            data.append(id, new VtuzxMap("materialNo", materialNumber).append("stockNo", stockNum).append("batchNo", batchNum).append("boxCount", boxCount).append("count", count));

            materialList.add(materialNumber);
            stockList.add(stockNum);
            ids.add(FastUtil.convertId(id));
        }

        List<VtuzxMap> detailList = new ArrayList<>();
        Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").in(ids).and("status").is("in"), new Document("createTime", -1), 0, 0);

        while (inventoryDetailItr.hasNext()) {
            Document detailDoc = inventoryDetailItr.next();
            VtuzxMap detailMap = new VtuzxMap(detailDoc);
            detailMap.append("inventoryId", VtuzxUtil.removeString(detailMap, "_inventoryId"));
            detailList.add(detailMap);
        }

        VtuzxMap materialMap = new VtuzxMap();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialList), null, 0, 0);
        while (materialItr.hasNext()) {
            Document mtr = materialItr.next();
            VtuzxMap mtrMap = new VtuzxMap();
            String materialNumber = VtuzxUtil.getString(mtr, "fNumber");
            String materialName = VtuzxUtil.getString(mtr, "fName");
            String fSpecification = VtuzxUtil.getString(mtr, "fSpecification");
            String fBaseUnitIdFName = VtuzxUtil.getString(mtr, "fBaseUnitIdFName");
            mtrMap.append("materialName", materialName);
            mtrMap.append("materialSpec", fSpecification);
            mtrMap.append("unit", fBaseUnitIdFName);
            mtrMap.append("materialNo", materialNumber);
            materialMap.append(materialNumber, mtrMap);
        }

        VtuzxMap stockMap = new VtuzxMap();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockList), null, 0, 0);
        while (stockItr.hasNext()) {
            Document stock = stockItr.next();
            VtuzxMap stkMap = new VtuzxMap();
            String stockNumber = VtuzxUtil.getString(stock, "fNumber");
            String stockName = VtuzxUtil.getString(stock, "fName");
            stkMap.append("stockNo", stockNumber);
            stkMap.append("stockName", stockName);
            stockMap.append(stockNumber, stkMap);
        }

        VtuzxMap res = new VtuzxMap();
        List<VtuzxMap> resList = new ArrayList<>();
        for (VtuzxMap detail: detailList) {
            String inventoryId = VtuzxUtil.getString(detail, "inventoryId");
            VtuzxMap inventoryMap = VtuzxUtil.getObject(data, inventoryId);

            String dMaterialNo = VtuzxUtil.getString(inventoryMap, "materialNo");
            VtuzxMap materials = VtuzxUtil.getObject(materialMap, dMaterialNo);
            String materialName = "";
            String materialSpec = "";
            String unit = "";
            if (!VtuzxUtil.isEmpty(materials)) {
                materialName = VtuzxUtil.getString(materials, "materialName");
                materialSpec = VtuzxUtil.getString(materials, "materialSpec");
                unit = VtuzxUtil.getString(materials, "unit");
            }

            String dLocationNo = VtuzxUtil.getString(detail, "locationNo");

            String dStockNo = VtuzxUtil.getString(detail, "stockNo");
            VtuzxMap stocks = VtuzxUtil.getObject(stockMap, dStockNo);
            String stockName = "";
            if (!VtuzxUtil.isEmpty(stocks)) {
                stockName = VtuzxUtil.getObject(stocks, "stockName");
            }


            String dBatchNo = VtuzxUtil.getString(inventoryMap, "batchNo");
            double count = VtuzxUtil.getDouble(detail, "count");
            String key = dMaterialNo + '_' + dLocationNo + '_' + dStockNo + '_' + dBatchNo;
            VtuzxMap resMap = VtuzxUtil.getObject(res, key);
            if (resMap == null) {
                VtuzxMap newDetail = new VtuzxMap("materialNo", dMaterialNo)
                        .append("materialName", materialName)
                        .append("materialSpec", materialSpec)
                        .append("unit", unit)
                        .append("locationNo", dLocationNo)
                        .append("stockNo", dStockNo)
                        .append("stockName", stockName)
                        .append("batchNo", dBatchNo)
                        .append("boxCount", 1)
                        .append("count", count);
                res.append(key, newDetail);
                resList.add(newDetail);
            } else {
                int resBoxCount = VtuzxUtil.getInt(resMap, "boxCount");
                double resCount = VtuzxUtil.getDouble(resMap, "count");
                resMap.append("boxCount", ++resBoxCount);
                resMap.append("count", resCount + count);
            }
        }




        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "仓库"),
                new VtuzxExcelCell(col++, "仓位"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "批次号"),
                new VtuzxExcelCell(col++, "库存箱数"),
                new VtuzxExcelCell(col++, "库存总量"),
                new VtuzxExcelCell(col, "单位")
        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : resList) {
//            VtuzxMap header = VtuzxUtil.getObject(detail, "header");
//            List<VtuzxMap> boxList = VtuzxUtil.getObject(detail, "boxList");

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "locationNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialSpec")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxCount")),
                    new VtuzxExcelCell(col++, new BigDecimal(VtuzxUtil.getString(detail, "count")).setScale(2, RoundingMode.HALF_UP).doubleValue()),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "unit"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 300},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 150},
                new int[]{7, 150},
                new int[]{8, 80}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("即时库存" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxMap downloadInventoryTray(VtuzxToken token, Map<String, Object> param) throws Exception {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String materialNo = VtuzxUtil.getString(param, "materialNo");
        String batchNo = VtuzxUtil.getString(param, "batchNo");
        List<String> produceDate = VtuzxUtil.getObject(param, "produceDate");
        Criteria cond = new Criteria("boxCount").gt(0);
        if (!VtuzxUtil.isEmpty(produceDate)) {
            cond.and("produceDate").gte(VtuzxUtil.parseDate(produceDate.get(0))).lte(VtuzxUtil.parseDate(produceDate.get(1)));
        }
        if (!VtuzxUtil.isEmpty(batchNo)) {
            cond.and("batchNo").regex("^" + batchNo + ".*$");
        }
        if (!VtuzxUtil.isEmpty(stockNo)) {
            cond.and("stockNo").regex(stockNo);
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            cond.and("materialNo").regex("^" + materialNo + ".*$");
        }
        Iterator<Document> inventoryItr = inventoryDao.findList(cond, new Document("createTime", 1), 0, 0);
        List<ObjectId> ids = new ArrayList<>();
        List<String> materialList = new ArrayList<>();
        List<String> stockList = new ArrayList<>();
        VtuzxMap data = new VtuzxMap();
        while (inventoryItr.hasNext()) {
            Document statDoc = inventoryItr.next();
            String id = VtuzxUtil.getString(statDoc, "_id");
            String materialNumber = VtuzxUtil.getString(statDoc, "materialNo");
            String stockNum = VtuzxUtil.getString(statDoc, "stockNo");
            String batchNum = VtuzxUtil.getString(statDoc, "batchNo");
            int boxCount = VtuzxUtil.getInt(statDoc, "boxCount");
            Double count = VtuzxUtil.getDouble(statDoc, "count");
            data.append(id, new VtuzxMap("materialNo", materialNumber).append("stockNo", stockNum).append("batchNo", batchNum).append("boxCount", boxCount).append("count", count));

            materialList.add(materialNumber);
            stockList.add(stockNum);
            ids.add(FastUtil.convertId(id));
        }

        List<VtuzxMap> detailList = new ArrayList<>();
        List<String> trayNumberList = new ArrayList<>();
        Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").in(ids).and("status").is("in"), new Document("createTime", -1), 0, 0);
        while (inventoryDetailItr.hasNext()) {
            Document detailDoc = inventoryDetailItr.next();
            VtuzxMap detailMap = new VtuzxMap(detailDoc);
            String trayNumber = VtuzxUtil.getString(detailMap, "trayNumber");
            detailMap.append("inventoryId", VtuzxUtil.removeString(detailMap, "_inventoryId"));
            detailList.add(detailMap);
            trayNumberList.add(trayNumber);
        }

        VtuzxMap materialMap = new VtuzxMap();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialList), null, 0, 0);
        while (materialItr.hasNext()) {
            Document mtr = materialItr.next();
            VtuzxMap mtrMap = new VtuzxMap();
            String materialNumber = VtuzxUtil.getString(mtr, "fNumber");
            String materialName = VtuzxUtil.getString(mtr, "fName");
            String fSpecification = VtuzxUtil.getString(mtr, "fSpecification");
            String fBaseUnitIdFName = VtuzxUtil.getString(mtr, "fBaseUnitIdFName");
            mtrMap.append("materialName", materialName);
            mtrMap.append("materialSpec", fSpecification);
            mtrMap.append("unit", fBaseUnitIdFName);
            mtrMap.append("materialNo", materialNumber);
            materialMap.append(materialNumber, mtrMap);
        }

        VtuzxMap stockMap = new VtuzxMap();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockList), null, 0, 0);
        while (stockItr.hasNext()) {
            Document stock = stockItr.next();
            VtuzxMap stkMap = new VtuzxMap();
            String stockNumber = VtuzxUtil.getString(stock, "fNumber");
            String stockName = VtuzxUtil.getString(stock, "fName");
            stkMap.append("stockNo", stockNumber);
            stkMap.append("stockName", stockName);
            stockMap.append(stockNumber, stkMap);
        }

        VtuzxMap trayMap = new VtuzxMap();
        Iterator<Document> trayItr = trayDao.findList(Criteria.where("trayNumber").in(trayNumberList), null, 0, 0);
        while (trayItr.hasNext()) {
            Document tray = trayItr.next();
            VtuzxMap map = new VtuzxMap();
            String trayNumber = VtuzxUtil.getString(tray, "trayNumber");
            String ip = VtuzxUtil.getString(tray, "ip");
            Date lastInventoryTime = VtuzxUtil.getObject(tray, "lastInventoryTime");
            map.append("trayNumber", trayNumber).append("lastInventoryTime", VtuzxUtil.formatYmdHmsF(lastInventoryTime)).append("ip", ip);
            trayMap.append(trayNumber, map);
        }

        for (VtuzxMap detail: detailList) {
            String inventoryId = VtuzxUtil.getString(detail, "inventoryId");
            String trayNumber = VtuzxUtil.getString(detail, "trayNumber");
            VtuzxMap tray = VtuzxUtil.getObject(trayMap, trayNumber);
            String lastInventoryTime = "";
            String ip = "";
            if (tray != null) {
                lastInventoryTime = VtuzxUtil.getString(tray, "lastInventoryTime");
                ip = VtuzxUtil.getString(tray, "ip");
            }
            VtuzxMap inventoryMap = VtuzxUtil.getObject(data, inventoryId);

            String dMaterialNo = VtuzxUtil.getString(inventoryMap, "materialNo");
            String dBatchNo = VtuzxUtil.getString(inventoryMap, "batchNo");
            VtuzxMap materials = VtuzxUtil.getObject(materialMap, dMaterialNo);
            String materialName = "";
            String materialSpec = "";
            String unit = "";
            if (!VtuzxUtil.isEmpty(materials)) {
                materialName = VtuzxUtil.getString(materials, "materialName");
                materialSpec = VtuzxUtil.getString(materials, "materialSpec");
                unit = VtuzxUtil.getString(materials, "unit");
            }


            String dStockNo = VtuzxUtil.getString(detail, "stockNo");
            VtuzxMap stocks = VtuzxUtil.getObject(stockMap, dStockNo);
            String stockName = "";
            if (!VtuzxUtil.isEmpty(stocks)) {
                stockName = VtuzxUtil.getObject(stocks, "stockName");
            }

            detail.append("lastInventoryTime", lastInventoryTime);
            detail.append("stockName", stockName);
            detail.append("materialNo", dMaterialNo);
            detail.append("batchNo", dBatchNo);
            detail.append("materialName", materialName);
            detail.append("materialSpec", materialSpec);
            detail.append("unit", unit);
            detail.append("ip", ip);

        }


        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "仓库"),
                new VtuzxExcelCell(col++, "仓位"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "批次号"),
                new VtuzxExcelCell(col++, "箱号"),
                new VtuzxExcelCell(col++, "重量"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "托盘信息"),
                new VtuzxExcelCell(col++, "托盘最后一次调用时间"),
                new VtuzxExcelCell(col, "ip")
        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : detailList) {
            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "locationNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialSpec")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxNo")),
                    new VtuzxExcelCell(col++, new BigDecimal(VtuzxUtil.getString(detail, "count")).setScale(2, RoundingMode.HALF_UP).doubleValue()),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "unit")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "trayNumber")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "lastInventoryTime")),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "ip"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 300},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 300},
                new int[]{7, 150},
                new int[]{8, 80},
                new int[]{9, 150},
                new int[]{10, 250},
                new int[]{11, 250}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("即时库存" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxDownloadBean downloadInventoryTotal(VtuzxToken token, Map<String, Object> param) throws Exception {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String materialNo = VtuzxUtil.getString(param, "materialNo");
        Criteria detailCond = new Criteria("status").is("in");
        if (!VtuzxUtil.isEmpty(stockNo)) {
            detailCond.and("stockNo").is(stockNo);
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            detailCond.and("inventory.materialNo").is(materialNo);
        }
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> inventoryDetailDoc = inventoryDetailDao.stockCount(detailCond);
        while(inventoryDetailDoc.hasNext()) {
            VtuzxMap data = new VtuzxMap();
            Document statDoc = inventoryDetailDoc.next();
            Document idDoc = VtuzxUtil.getObject(statDoc, "_id");
            String materialNum = VtuzxUtil.getString(idDoc, "materialNo");
            String stockNum = VtuzxUtil.getString(idDoc, "stockNo");
            int boxCount = VtuzxUtil.getInt(statDoc, "totalBoxCount");
            double count = VtuzxUtil.getDouble(statDoc, "totalCount");
            data.append("materialNo", materialNum).append("stockNo", stockNum).append("boxCount", boxCount).append("count", count);
            dataList.add(data);
        }

        for (VtuzxMap data: dataList) {
            String materialNum = VtuzxUtil.getString(data, "materialNo");
            String stockNum = VtuzxUtil.getString(data, "stockNo");
            Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNum));
            String materialSpec = VtuzxUtil.getString(materialDoc, "fSpecification");
            String materialName = VtuzxUtil.getString(materialDoc, "fName");
            data.append("materialSpec", materialSpec).append("materialName", materialName);
            Document stockDoc = stockDao.findOne(Criteria.where("fNumber").is(stockNum));
            String stockName = VtuzxUtil.getString(stockDoc, "fName");
            data.append("stockName", stockName);
        }
        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
        List<VtuzxExcelSheet> sheets = new ArrayList<>();
            VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存总重");
            int col = 0;
            int row = 0;
            VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, "仓库"),
                    new VtuzxExcelCell(col++, "物料编码"),
                    new VtuzxExcelCell(col++, "物料名称"),
                    new VtuzxExcelCell(col++, "物料规格"),
                    new VtuzxExcelCell(col, "总重")
            ));
            sheet0.addRow(header0);

        for(VtuzxMap data : dataList){

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "stockName") + ":" + VtuzxUtil.getString(data, "stockNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialSpec")),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(data, "count"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 300},
                new int[]{3, 200},
                new int[]{4, 200}
        ));
        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("物料即时库存总重" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxDownloadBean downloadInventoryDetail(VtuzxToken token, Map<String, Object> param) throws Exception {
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        String materialNo = VtuzxUtil.getString(param, "materialNo");
        String batchNo = VtuzxUtil.getString(param, "batchNo");
        List<String> produceDate = VtuzxUtil.getObject(param, "produceDate");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(produceDate)) {
            cond.and("produceDate").gte(VtuzxUtil.parseDate(produceDate.get(0))).lte(VtuzxUtil.parseDate(produceDate.get(1)));
        }
        if (!VtuzxUtil.isEmpty(batchNo)) {
            cond.and("batchNo").regex("^" + batchNo + ".*$");
        }
        if (!VtuzxUtil.isEmpty(stockNo)) {
            cond.and("stockNo").regex(stockNo);
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            cond.and("materialNo").regex("^" + materialNo + ".*$");
        }
        List<String> materialNoList = new ArrayList<>();
        Iterator<Document> inventoryItr = inventoryDao.findList(cond, new Document("createTime", -1), 0, 0);
        List<Document> dataList = new ArrayList<>();

        List<ObjectId> _detailIdList = new ArrayList<>();
        while (inventoryItr.hasNext()) {
            Document data = inventoryItr.next();
            _detailIdList.add(VtuzxUtil.getObject(data, IVtuzxConst.Key__id));
            String materialNumber = VtuzxUtil.getString(data, "materialNo");
            if (!materialNoList.contains(materialNumber)) {
                materialNoList.add(materialNumber);
            }
            dataList.add(data);
        }
        Iterator<Document> stockItr = stockDao.findList(null, null, 0, 0);
        VtuzxMap stockMap = FastUtil.itr2Map(stockItr, "fNumber");
        Iterator<Document> stockDetailItr = stockDetailDao.findList(null, null, 0, 0);
        VtuzxMap stockDetailMap = FastUtil.itr2Map(stockDetailItr, "locationNo");
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList), null, 0, 0);
        VtuzxMap materialMap = FastUtil.itr2Map(materialItr, "fNumber");

        Iterator<Document> detailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").in(_detailIdList).and("status").is("in"), new Document("_detailId", 1).append("boxNo", 1), 0, 0);
        VtuzxMap detailMap = new VtuzxMap();
        while (detailItr.hasNext()) {
            Document detail = detailItr.next();
            String inventoryId = VtuzxUtil.getString(detail, "_inventoryId");
            String locationNo = VtuzxUtil.getString(detail, "locationNo");
            Document location = VtuzxUtil.getObject(stockDetailMap, locationNo);
            if (location != null) {
                detail.append("locationName", VtuzxUtil.getString(location, "fName"));
            }
            List<Document> details = VtuzxUtil.getObject(detailMap, inventoryId);
            if (details == null) {
                details = new ArrayList<>();
                detailMap.append(inventoryId, details);
            }
            details.add(detail);
        }
        for (Document data : dataList) {
            String materialNumber = VtuzxUtil.getString(data, "materialNo");
            Document materialDoc = VtuzxUtil.getObject(materialMap, materialNumber);
            if (materialDoc != null) {
                String materialName = VtuzxUtil.getString(materialDoc, "fName");
                String fSpecification = VtuzxUtil.getString(materialDoc, "fSpecification");
                String fBaseUnitIdFName = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
                data.append("materialName", materialName);
                data.append("materialSpec", fSpecification);
                data.append("unit", fBaseUnitIdFName);
            }
            Document stockDoc = VtuzxUtil.getObject(stockMap, VtuzxUtil.getString(data, "stockNo"));
            if (stockDoc != null) {
                String fName = VtuzxUtil.getString(stockDoc, "fName");
                data.append("stockName", fName);
            }
        }

        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "仓库"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "批次号"),
                new VtuzxExcelCell(col++, "库存箱数"),
                new VtuzxExcelCell(col++, "库存总量"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "箱号"),
                new VtuzxExcelCell(col++, "仓位"),
                new VtuzxExcelCell(col, "重量")
        ));
        sheet0.addRow(header0);

        for (Document data : dataList) {
            String detailId = VtuzxUtil.getString(data, IVtuzxConst.Key__id);
            List<Document> details = VtuzxUtil.getObject(detailMap, detailId);
            if (details == null) {
                continue;
            }
            for (Document detail : details) {
                col = 0;
                VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "stockName")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialNo")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialName")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "materialSpec")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "batchNo")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "boxCount")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "count")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(data, "unit")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxNo")),
                        new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "locationName")),
                        new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "count"))
                ));
                sheet0.addRow(dataRow);
            }
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 300},
                new int[]{3, 200},
                new int[]{4, 200},
                new int[]{5, 150},
                new int[]{6, 150},
                new int[]{7, 80},
                new int[]{8, 200},
                new int[]{9, 200},
                new int[]{10, 150}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("即时库存" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxMap getInventoryDetail(VtuzxToken token, Map<String, Object> param) {
        List<VtuzxMap> dataList = new ArrayList<>();
        VtuzxMap header = new VtuzxMap();
        ObjectId _id = new ObjectId(VtuzxUtil.getString(param, "id"));
        Document inventoryDoc = inventoryDao.findById(_id);
        String stockNo = VtuzxUtil.getString(inventoryDoc, "stockNo");
        String materialNo = VtuzxUtil.getString(inventoryDoc, "materialNo");
        Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNo));
        String materialName = VtuzxUtil.getString(materialDoc, "fName");
        String materialSpec = VtuzxUtil.getString(materialDoc, "fSpecification");
        String fBaseUnitIdFName = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
        String batchNo = VtuzxUtil.getString(inventoryDoc, "batchNo");
        int boxCount = VtuzxUtil.getInt(inventoryDoc, "boxCount");
        double count = VtuzxUtil.getDouble(inventoryDoc, "count");
        header.append("stockNo", stockNo)
                .append("materialNo", materialNo)
                .append("materialName", materialName)
                .append("materialSpec", materialSpec)
                .append("batchNo", batchNo)
                .append("unit", fBaseUnitIdFName)
                .append("boxCount", boxCount)
                .append("count", count);
        Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").is(_id), new Document("createTime", -1), 0, 0);
        while (inventoryDetailItr.hasNext()) {
            Document inventoryDetailDoc = inventoryDetailItr.next();
            VtuzxMap inventoryMap = new VtuzxMap(inventoryDetailDoc);
            dataList.add(inventoryMap);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList).append("header", header);
    }

    /**
     * 保存库存物料
     *
     * @param _userId             用户ID
     * @param userName            用户名
     * @param stockNo             仓库编码
     * @param event               单据动作
     * @param billType            单据类型
     * @param billNo              单据号
     * @param _inventoryId        库存ID
     * @param countDoc            库存数量
     * @param updatedInventoryMap 更新库存数量
     */
    private void reduceInventoryMaterial(ObjectId _userId, String userName,
                                         String transactionKey, String stockNo, BillEvent event, BillType billType, String billNo,
                                         ObjectId _inventoryId, Document countDoc, Map<ObjectId, Document> updatedInventoryMap) {

        List<Document> boxList = VtuzxUtil.removeObject(countDoc, "boxList");

        Document inventoryDoc = inventoryDao.findById(_inventoryId);
        Document inventoryHistory = new Document("transactionKey", transactionKey)
                .append("_inventoryId", _inventoryId)
                .append("billNo", billNo)
                .append("billType", billType.toString())
                .append("billEvent", event.toString())
                .append("stockNo", stockNo)
                .append("materialNo", VtuzxUtil.getObject(inventoryDoc, "materialNo"))
                .append("batchNo", VtuzxUtil.getObject(inventoryDoc, "batchNo"))
                .append("produceDate", VtuzxUtil.getObject(inventoryDoc, "produceDate"))
                .append("exp", VtuzxUtil.getObject(inventoryDoc, "exp"))
                .append("unit", VtuzxUtil.getObject(inventoryDoc, "unit"))
                .append("boxCount", VtuzxUtil.getObject(countDoc, "boxCount"))
                .append("count", VtuzxUtil.getObject(countDoc, "count"));
        inventoryDao.updateStock(_userId, userName, _inventoryId, countDoc);

        updatedInventoryMap.put(_inventoryId, countDoc);
        ObjectId _historyId = inventoryHistoryDao.insertHistory(_userId, userName, inventoryHistory);

        List<Document> historyDetailDocList = new ArrayList<>();
        for (Document box : boxList) {
            Document historyDetailDoc = new Document("transactionKey", transactionKey)
                    .append("boxNo", VtuzxUtil.getString(box, "boxNo"))
                    .append("count", VtuzxUtil.getDouble(box, "count") * -1)
                    .append("stockNo", stockNo)
                    .append("locationNo", VtuzxUtil.getString(box, "locationNo"))
                    .append("status", StockStatus.Out.toString())
                    .append("_historyId", _historyId)
                    .append("_inventoryId", _inventoryId)
                    .append("traceBack", VtuzxUtil.getObject(box, "traceBack"));
            historyDetailDocList.add(historyDetailDoc);
        }
        inventoryHistoryDetailDao.insertDetail(_userId, userName, historyDetailDocList);
    }

    @Override
    public VtuzxMap getMaterialByRfidInStock(List<String> rfidList, String stockNo, String locationNo) {
        Document trayDoc = trayDao.findOne(Criteria.where("rfid").in(rfidList));
        if (!VtuzxUtil.isEmpty(trayDoc)) {
            String trayNumber = VtuzxUtil.getString(trayDoc, "trayNumber");
            Iterator<Document> boxItr = inventoryDetailDao.findList(Criteria.where("trayNumber").is(trayNumber).and("status").is("in"), null, 0, 0);
            while (boxItr.hasNext()) {
                Document box = boxItr.next();
                String rfid = VtuzxUtil.getString(box, "rfid");
                rfidList.add(rfid);
            }
        }

        Criteria cond = Criteria.where("rfid").in(rfidList).and("status").is("in").and("stockNo").is(stockNo);
        if (!VtuzxUtil.isEmpty(locationNo)) {
            cond.and("locationNo").is(locationNo);
        }
        Iterator<Document> boxItr = inventoryDetailDao.findList(Criteria.where("rfid").in(rfidList).and("status").is("in").and("stockNo").is(stockNo), null, 0, 0);
        List<Document> boxList = new ArrayList<>();
        List<ObjectId> inventoryIdList = new ArrayList<>();
        while (boxItr.hasNext()) {
            Document box = boxItr.next();
            ObjectId _inventoryId = VtuzxUtil.getObject(box, "_inventoryId");
            boxList.add(box);
            if (!inventoryIdList.contains(_inventoryId)) {
                inventoryIdList.add(_inventoryId);
            }
        }
        if (VtuzxUtil.isEmpty(boxList)) {
            return new VtuzxMap(IVtuzxConst.Key_data, new ArrayList<>());
        }
        Iterator<Document> inventoryItr = inventoryDao.findList(Criteria.where(IVtuzxConst.Key__id).in(inventoryIdList), null, 0, 0);
        List<String> materialNoList = new ArrayList<>();
        VtuzxMap inventoryMap = new VtuzxMap();
        while (inventoryItr.hasNext()) {
            Document inventoryDoc = inventoryItr.next();
            String materialNo = VtuzxUtil.getString(inventoryDoc, "materialNo");
            inventoryMap.append(VtuzxUtil.getString(inventoryDoc, IVtuzxConst.Key__id), inventoryDoc);
            if (!materialNoList.contains(materialNo)) {
                materialNoList.add(materialNo);
            }
        }
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList), null, 0, 0);
        VtuzxMap materialMap = FastUtil.itr2Map(materialItr, "fNumber");

        List<VtuzxMap> retList = new ArrayList<>();
        for (Document box : boxList) {
            String inventoryId = VtuzxUtil.getString(box, "_inventoryId");
            Document inventory = VtuzxUtil.getObject(inventoryMap, inventoryId);
            if (inventory == null) continue;
            String materialNo = VtuzxUtil.getString(inventory, "materialNo");
            Document material = VtuzxUtil.getObject(materialMap, materialNo);
            if (material == null) continue;
            String materialName = VtuzxUtil.getString(material, "fName");
            String materialSpec = VtuzxUtil.getString(material, "fSpecification");
            String unit = VtuzxUtil.getString(material, "fBaseUnitIdNumber");
            String unitName = VtuzxUtil.getString(material, "fBaseUnitIdFName");
            VtuzxMap retBox = new VtuzxMap(box).append("materialNo", materialNo)
                    .append("materialName", materialName)
                    .append("materialSpec", materialSpec)
                    .append("unit", unit)
                    .append("unitName", unitName);
            retBox.remove(IVtuzxConst.Key__id);
            retBox.remove(IVtuzxConst.Key_createTime);
            retBox.remove(IVtuzxConst.Key_updateTime);
            retBox.remove("_inventoryId");
            retBox.remove("_createUserId");
            retBox.remove("createUserName");
            retBox.remove("_updateUserId");
            retBox.remove("updateUserName");
            retBox.remove("transactionKey");
            retBox.remove("status");
            retList.add(retBox);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, retList);
    }

    @Override
    public VtuzxMap getBatchAndExpMaterialList(String batchNo, String materialNo, String stockNo, int exp) {
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(batchNo)) {
            cond.and("batchNo").is(batchNo);
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            cond.and("materialNo").is(materialNo);
        }
        if (!VtuzxUtil.isEmpty(stockNo)) {
            cond.and("stockNo").is(stockNo);
        }
        if (exp != 0) {
            cond.and("exp").is(exp);
        }

        VtuzxMap materials = new VtuzxMap();
        List<ObjectId> inventoryIdList = new ArrayList<>();
        Iterator<Document> materialItr = inventoryDao.findList(cond, null, 0, 0);
        while (materialItr.hasNext()) {
            Document materialDoc = materialItr.next();
            VtuzxMap materialMap = new VtuzxMap(materialDoc);
            materialMap.append("id", VtuzxUtil.removeString(materialMap, "_id"));
            inventoryIdList.add(VtuzxUtil.getObject(materialDoc, IVtuzxConst.Key__id));
            String id = VtuzxUtil.getString(materialDoc, IVtuzxConst.Key__id);
            materials.append(id, materialMap);
        }

        List<VtuzxMap> materialDetailList = new ArrayList<>();
        Iterator<Document> materialDetailItr = inventoryDetailDao.findList(Criteria.where("_inventoryId").in(inventoryIdList).and("status").is("in"), null, 0, 0);
        while (materialDetailItr.hasNext()) {
            Document materialDetailDoc = materialDetailItr.next();
            VtuzxMap detailMap = new VtuzxMap(materialDetailDoc);
            detailMap.append("id", VtuzxUtil.removeString(detailMap, "_id"));
            detailMap.append("inventoryId", VtuzxUtil.removeString(detailMap, "_inventoryId"));
            detailMap.append("createUserId", VtuzxUtil.removeString(detailMap, "_createUserId"));
            detailMap.append("updateUserId", VtuzxUtil.removeString(detailMap, "_updateUserId"));
            materialDetailList.add(detailMap);
        }

        for (VtuzxMap materialDetail: materialDetailList) {
            String inventoryId = VtuzxUtil.getString(materialDetail, "inventoryId");
            VtuzxMap inventoryMap = VtuzxUtil.getObject(materials, inventoryId);
            String inventoryMaterialNo = VtuzxUtil.getString(inventoryMap, "materialNo");
            String inventoryBatchNo = VtuzxUtil.getString(inventoryMap, "batchNo");
            int inventoryExp = VtuzxUtil.getInt(inventoryMap, "exp");
            Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(inventoryMaterialNo));
            String materialName = VtuzxUtil.getString(materialDoc, "fName");
            String materialSpec = VtuzxUtil.getString(materialDoc, "fSpecification");
            String unit = VtuzxUtil.getString(materialDoc, "fBaseUnitIdNumber");
            String unitName = VtuzxUtil.getString(materialDoc, "fBaseUnitIdFName");
            materialDetail.append("materialNo", inventoryMaterialNo);
            materialDetail.append("materialName", materialName);
            materialDetail.append("materialSpec", materialSpec);
            materialDetail.append("batchNo", inventoryBatchNo);
            materialDetail.append("exp", inventoryExp);
            materialDetail.append("unit", unit);
            materialDetail.append("unitName", unitName);
        }

        return new VtuzxMap(IVtuzxConst.Key_data, materialDetailList);
    }

    @Override
    public void saveBatchAndExpBill(ObjectId _userId, String name, List<Map<String, Object>> boxList) throws VtuzxException {
        Map<String, Object> box = boxList.get(0);
        String batchNo = VtuzxUtil.getString(box, "batchNo");
        int exp = VtuzxUtil.getInt(box, "exp");

        for (Map<String, Object> boxs: boxList) {
            Document data = new Document(boxs);
            String stockNo = VtuzxUtil.getString(data, "stockNo");
            String locationNo = VtuzxUtil.getString(data, "locationNo");
            String materialNo = VtuzxUtil.getString(data, "materialNo");
            String materialName = VtuzxUtil.getString(data, "materialName");
            String materialSpec = VtuzxUtil.getString(data, "materialSpec");
            String dataBatchNo = VtuzxUtil.getString(data, "batchNo");
            double count = VtuzxUtil.getDouble(data, "count");
            int dataExp = VtuzxUtil.getInt(data, "exp");
            String boxNo = VtuzxUtil.getString(data, "boxNo");
            String rfid = VtuzxUtil.getString(data, "rfid");
            String unit = VtuzxUtil.getString(data, "unit");
            String unitName = VtuzxUtil.getString(data, "unitName");

            Document boxMap = batchAndExpDetailBoxDao.findOne(Criteria.where("boxNo").is(boxNo).and("isTemp").is(true));
            if (!VtuzxUtil.isEmpty(boxMap)) {
                String createUserName = VtuzxUtil.getString(boxMap, "createUserName");
                throw new VtuzxException("箱号已存在，请勿重复添加！【扫码人员：" + createUserName + "】");
            }

            Criteria detailCond = Criteria.where("stockNo").is(stockNo);
            detailCond.and("isTemp").is(true);
            detailCond.and("materialNo").is(materialNo);
            detailCond.and("batchNo").is(dataBatchNo);
            detailCond.and("exp").is(dataExp);
            Document detail = batchAndExpDetailDao.findOne(detailCond);
            Document updateVal = new Document();

            if (detail == null) {
                updateVal.append("isTemp", true);
//                updateVal.append("billNo", billNo);
                updateVal.append("stockNo", stockNo);
                updateVal.append("materialNo", materialNo);
                updateVal.append("materialName", materialName);
                updateVal.append("materialSpec", materialSpec);
                updateVal.append("batchNo", batchNo);
                updateVal.append("_createUserId", _userId);
                updateVal.append("createUserName", name);
                updateVal.append("boxCount", 1);
                updateVal.append("count", count);
                updateVal.append("exp", exp);
                updateVal.append("unit", unit);
                updateVal.append("unitName", unitName);
            } else {
                updateVal = new Document("$inc", new Document("boxCount", 1).append("count", count));
            }
            ObjectId _detailId;
            if (detail == null) {
                _detailId = batchAndExpDetailDao.insertOne(updateVal);
            } else {
                _detailId = VtuzxUtil.getObject(detail, IVtuzxConst.Key__id);
                batchAndExpDetailDao.updateByIdRedirect(_detailId, updateVal);
            }

            Document detailBox = new Document();
            detailBox.append("isTemp", true);
            detailBox.append("_detailId", _detailId);
//            detailBox.append("billNo", billNo);
            detailBox.append("materialNo", materialNo);
            detailBox.append("materialName", materialName);
            detailBox.append("materialSpec", materialSpec);
            detailBox.append("boxNo", boxNo);
            detailBox.append("count", count);
            detailBox.append("batchNo", batchNo);
            detailBox.append("_createUserId", _userId);
            detailBox.append("createUserName", name);
            detailBox.append("stockNo", stockNo);
            detailBox.append("locationNo", locationNo);
            detailBox.append("rfid", rfid);
            detailBox.append("exp", exp);
            batchAndExpDetailBoxDao.insertOne(detailBox);

        }

    }



    @Override
    public VtuzxMap createBatchAndExpBill(ObjectId userId, String name, String updateBatchNo, int updateExp, List<Map<String, Object>> selectMaterialList) throws VtuzxException {
        Map<String, Object> selectMaterial = selectMaterialList.get(0);
        if (VtuzxUtil.isEmpty(selectMaterial)) {
            throw new VtuzxException("当前未选择需要调整的物料");
        }
        String batchNo = VtuzxUtil.getString(selectMaterial, "batchNo");
        int exp = VtuzxUtil.getInt(selectMaterial, "exp");
        String billNo = IXqcConst.createBillNo(BillType.TZDD);
        Document doc = new Document("billNo", billNo)
                .append("fromBatchNo", batchNo)
                .append("fromExp", exp)
                .append("batchNo", updateBatchNo)
                .append("exp", updateExp)
                .append("status", BillStatus.Save.toString());
        batchAndExpDao.insertBill(userId, name, doc);

        batchAndExpDetailDao.updateManyRedirect(Criteria.where("isTemp").is(true).and("_createUserId").is(userId),
                new Document("$unset", new Document("isTemp", 1))
                        .append("$set", new Document("billNo", billNo).append("updateBatchNo", updateBatchNo).append("updateExp", updateExp)));
        batchAndExpDetailBoxDao.updateManyRedirect(Criteria.where("isTemp").is(true).and("_createUserId").is(userId),
                new Document("$unset", new Document("isTemp", 1))
                        .append("$set", new Document("billNo", billNo)));
        return new VtuzxMap("billNo", billNo);
    }


    @Override
    public VtuzxMap auditBatchAndExpBill(ObjectId _userId, String name, String billNo) throws VtuzxException {
        List<BillMaterial> batchAndExpDetailList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        List<VtuzxMap> bcBoxList = new ArrayList<>();
        Criteria criteria = new Criteria("billNo").is(billNo);
        List<Document> batchAndExpList= new ArrayList<>();//存detail层数据
        Iterator<Document> batchAndExpDetailItr = batchAndExpDetailDao.findList(criteria, null, 0, 0);
        Iterator<Document> batchAndExpDetailBoxItr = batchAndExpDetailBoxDao.findList(criteria, null, 0, 0);
        List<Document> batchAndExpDetailBoxList = new ArrayList<>();
        while (batchAndExpDetailBoxItr.hasNext()) {
            Document batchAndExpDetailBoxDoc = batchAndExpDetailBoxItr.next();
            batchAndExpDetailBoxList.add(batchAndExpDetailBoxDoc);
        }

        while (batchAndExpDetailItr.hasNext()) {
            Document batchAndExpDetailDoc = batchAndExpDetailItr.next();
            BillMaterial billMaterial = new BillMaterial();
            ObjectId _batchAndExpDetailId = batchAndExpDetailDoc.getObjectId(IVtuzxConst.Key__id);
            billMaterial.setMaterialNo(VtuzxUtil.getString(batchAndExpDetailDoc, "materialNo"));
            billMaterial.setUnit(VtuzxUtil.getString(batchAndExpDetailDoc, "unit"));
            billMaterial.setBatchNo(VtuzxUtil.getString(batchAndExpDetailDoc, "updateBatchNo"));
            billMaterial.setProduceDate(VtuzxUtil.getString(batchAndExpDetailDoc, "produceDate"));
            billMaterial.setExp(VtuzxUtil.getInt(batchAndExpDetailDoc, "updateExp"));
            billMaterial.setStockNo(VtuzxUtil.getString(batchAndExpDetailDoc, "stockNo"));
            List<BillMaterialBox> boxList = new ArrayList<>();
            batchAndExpList.add(batchAndExpDetailDoc);
            for (Document batchAndExpDetailBox: batchAndExpDetailBoxList) {
                ObjectId detailId = VtuzxUtil.getObject(batchAndExpDetailBox, "_detailId");
                if (detailId.equals(_batchAndExpDetailId)) {
                    BillMaterialBox box = new BillMaterialBox();
                    String boxNo = VtuzxUtil.getString(batchAndExpDetailBox, "boxNo");
                    box.setBoxNo(boxNo);
                    box.setRfid(VtuzxUtil.getString(batchAndExpDetailBox, "rfid"));
                    double count = VtuzxUtil.getDouble(batchAndExpDetailBox, "count");
                    box.setCount(count);
                    box.setLocationNo(VtuzxUtil.getString(batchAndExpDetailBox, "locationNo"));
                    boxList.add(box);
                    String[] boxValues = boxNo.split("\\|\\;");
                    if (boxValues.length == 3) {
                        // 已入库包材长度为3
                        bcBoxList.add(new VtuzxMap("boxNo", boxNo).append("count", BigDecimal.valueOf(count).setScale(2, RoundingMode.HALF_UP).doubleValue()));
                    } else {
                        boxNoList.add(boxNo);
                    }
                }
                billMaterial.setBoxList(boxList);
            }
            batchAndExpDetailList.add(billMaterial);

        }
        batchAndExpDao.audit(_userId, name, billNo);
        try {
            inventoryService.reduceInventory(_userId, name, BillEvent.Audit, BillType.DBCK, billNo, boxNoList, bcBoxList);
            inventoryService.addInventory(_userId, name, BillEvent.Audit, BillType.DBRK, billNo, batchAndExpDetailList);
        } catch(Exception e) {
            batchAndExpDao.updateOne(Criteria.where("billNo").is(billNo), new Document("status", BillStatus.Save.toString()));
            throw new VtuzxException(e.getMessage());
        }
        //传金蝶
        try {
            syncBatchAndExp(_userId,name,batchAndExpList);
        } catch (Exception e){
            _logger.info(e.getMessage());
        }
        return null;
    }

    //传吉客云批号调整单
    private void syncBatchAndExp(ObjectId userId,String name,List<Document> batchAndExpList) throws Exception {
        //金蝶token
        VtuzxKingdeeCloudClient client = kingdeeService.loginKingdee();
        Map<String, Object> batchParam = new HashMap<>();//传单param
        Map<String, Object> batchModel = new HashMap<>();//单据头
        List<Map<String, Object>> batchList = new ArrayList<>();//单据体
        String billNo = VtuzxUtil.getString(batchAndExpList.get(0),"billNo");
        int groupNum = 1;//但具体分组字段
        for (Document batchDoc : batchAndExpList ){
            String batchNo = VtuzxUtil.getString(batchDoc,"batchNo");
            String unitName = VtuzxUtil.getString(batchDoc,"unitName");
            String materialNo = VtuzxUtil.getString(batchDoc, "materialNo");
            double count = VtuzxUtil.getDouble(batchDoc,"count");
            String stockNo = VtuzxUtil.getString(batchDoc,"stockNo");
            String updateBatchNo = VtuzxUtil.getString(batchDoc,"updateBatchNo");

            //查询单位编码，直接传文字无效
            String cond = String.format("Fname = '%s'", unitName);
            List<VtuzxMap> bd_customer = kingdeeService.query(client, "BD_UNIT", new VtuzxArrayList<>(
                    "FNumber",//单位编码
                    "FNAME"//单位名称
            ), cond);
            String unit = "";
            if (bd_customer != null && bd_customer.size() > 0) {
                unit = bd_customer.get(0).getString("FNumber");
            }

            //转换前
            Map<String,Object> batchEntry = new LinkedHashMap<>();
            batchEntry.put("FGroup",groupNum);
            batchEntry.put("FConvertType","A");//转换类型
            batchEntry.put("FMATERIALID",new Document("FNumber",materialNo));//物料
            batchEntry.put("FLot",new Document("FNumber",batchNo));//批号
            batchEntry.put("FUnitID",new Document("FNumber",unit));//单位
            batchEntry.put("FQty",count);//转换数量
            batchEntry.put("FSTOCKID",new Document("FNumber",stockNo));//仓库
            batchEntry.put("FStockStatusId",new Document("FNumber","KCZT01_SYS"));//库存状态
            batchEntry.put("FKeeperTypeId","BD_KeeperOrg");//保管者类型
            batchEntry.put("FKeeperId",new Document("FNumber","100"));//保管者
            batchEntry.put("FOwnerTypeId","BD_OwnerOrg");//货主类型
            batchEntry.put("FOwnerId",new Document("FNumber","100"));//货主
            batchList.add(batchEntry);

            //转换后
            Map<String,Object> afterEntry = new LinkedHashMap<>();
            afterEntry.put("FGroup",groupNum);
            afterEntry.put("FConvertType","B");//转换类型
            afterEntry.put("FMATERIALID",new Document("FNumber",materialNo));//物料
            afterEntry.put("FLot",new Document("FNumber",updateBatchNo));//批号
            afterEntry.put("FUnitID",new Document("FNumber",unit));//单位
            afterEntry.put("FQty",count);//转换数量
            afterEntry.put("FSTOCKID",new Document("FNumber",stockNo));//仓库
            afterEntry.put("FStockStatusId",new Document("FNumber","KCZT01_SYS"));//库存状态
            afterEntry.put("FKeeperTypeId","BD_KeeperOrg");//保管者类型
            afterEntry.put("FKeeperId",new Document("FNumber","100"));//保管者
            afterEntry.put("FOwnerTypeId","BD_OwnerOrg");//货主类型
            afterEntry.put("FOwnerId",new Document("FNumber","100"));//货主
            batchList.add(afterEntry);
            groupNum++;
        }

        batchModel.put("FID",0);//实体主键
        batchModel.put("FBillTypeID",new Document("FNUMBER","PHTZ01_SYS"));//单据类型
        batchModel.put("FStockOrgId",new Document("FNumber","100"));//库存组织
        batchModel.put("FDate",VtuzxUtil.formatYmdHmsF(new Date()));
        batchModel.put("FOwnerTypeIdHead","BD_OwnerOrg");//货主类型
        batchModel.put("FOwnerIdHead",new Document("FNumber","100"));//
        batchModel.put("F_ora_ZTDJH",billNo);//中台单据号
        batchModel.put("FSTK_LOTADJUSTENTRY",batchList);

        batchParam.put("NeedUpDateFields", new ArrayList<>());
        batchParam.put("NeedReturnFields", new ArrayList<>());
        batchParam.put("IsDeleteEntry", true);
        batchParam.put("SubSystemId", "");
        batchParam.put("IsVerifyBaseDataField", false);
        batchParam.put("IsEntryBatchFill", true);
        batchParam.put("ValidateFlag", true);
        batchParam.put("NumberSearch", true);
        batchParam.put("InterationFlags", "");
        batchParam.put("Model", batchModel);

        try {
            List<VtuzxMap> inStockList = kingdeeService.save(client, "STK_LOTADJUST", batchParam);

            VtuzxMap prdInMap = inStockList.get(0);
            String inNumber = VtuzxUtil.getString(prdInMap, "Number");
            kingdeeService.submit(client, "STK_LOTADJUST", "0", inNumber);
            kingdeeService.audit(client, "STK_LOTADJUST", "0", inNumber, "STK_InvCheckResult");
        } catch (Exception e){
            _logger.info(e.getMessage());
        }

    }

    @Override
    public VtuzxMap unauditBatchAndExpBill(ObjectId userId, String name, String billNo) throws VtuzxException {
        List<BillMaterial> batchAndExpDetailList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        List<VtuzxMap> bcBoxList = new ArrayList<>();
        Criteria criteria = new Criteria("billNo").is(billNo);
        Iterator<Document> batchAndExpDetailItr = batchAndExpDetailDao.findList(criteria, null, 0, 0);
        Iterator<Document> batchAndExpDetailBoxItr = batchAndExpDetailBoxDao.findList(criteria, null, 0, 0);
        List<Document> batchAndExpDetailBoxList = new ArrayList<>();
        while (batchAndExpDetailBoxItr.hasNext()) {
            Document batchAndExpDetailBoxDoc = batchAndExpDetailBoxItr.next();
            batchAndExpDetailBoxList.add(batchAndExpDetailBoxDoc);
        }

        while (batchAndExpDetailItr.hasNext()) {
            Document batchAndExpDetailDoc = batchAndExpDetailItr.next();
            BillMaterial billMaterial = new BillMaterial();
            ObjectId _batchAndExpDetailId = batchAndExpDetailDoc.getObjectId(IVtuzxConst.Key__id);
            billMaterial.setMaterialNo(VtuzxUtil.getString(batchAndExpDetailDoc, "materialNo"));
            billMaterial.setUnit(VtuzxUtil.getString(batchAndExpDetailDoc, "unit"));
            billMaterial.setBatchNo(VtuzxUtil.getString(batchAndExpDetailDoc, "batchNo"));
            billMaterial.setProduceDate(VtuzxUtil.getString(batchAndExpDetailDoc, "produceDate"));
            billMaterial.setExp(VtuzxUtil.getInt(batchAndExpDetailDoc, "exp"));
            billMaterial.setStockNo(VtuzxUtil.getString(batchAndExpDetailDoc, "stockNo"));
            List<BillMaterialBox> boxList = new ArrayList<>();
            for (Document batchAndExpDetailBox: batchAndExpDetailBoxList) {
                ObjectId detailId = VtuzxUtil.getObject(batchAndExpDetailBox, "_detailId");
                if (detailId.equals(_batchAndExpDetailId)) {
                    BillMaterialBox box = new BillMaterialBox();
                    String boxNo = VtuzxUtil.getString(batchAndExpDetailBox, "boxNo");
                    box.setBoxNo(boxNo);
                    box.setRfid(VtuzxUtil.getString(batchAndExpDetailBox, "rfid"));
                    double count = VtuzxUtil.getDouble(batchAndExpDetailBox, "count");
                    box.setCount(count);
                    box.setLocationNo(VtuzxUtil.getString(batchAndExpDetailBox, "locationNo"));
                    boxList.add(box);
                    String[] boxValues = boxNo.split("\\|\\;");
                    if (boxValues.length == 3) {
                        // 已入库包材长度为3
                        bcBoxList.add(new VtuzxMap("boxNo", boxNo).append("count", BigDecimal.valueOf(count).setScale(2, RoundingMode.HALF_UP).doubleValue()));
                    } else {
                        boxNoList.add(boxNo);
                    }
                }
                billMaterial.setBoxList(boxList);
            }
            batchAndExpDetailList.add(billMaterial);

        }
        batchAndExpDao.updateOneRedirect(Criteria.where("billNo").is(billNo), new Document("$set", new Document("status", BillStatus.Save.toString()))
                .append("$unset", new Document("auditUserName", 1).append("_auditUserId", 1).append("auditTime", 1)));
        try {
            inventoryService.reduceInventory(userId, name, BillEvent.Audit, BillType.DBCK, billNo, boxNoList, bcBoxList);
            inventoryService.addInventory(userId, name, BillEvent.Audit, BillType.DBRK, billNo, batchAndExpDetailList);

        } catch(Exception e) {
            batchAndExpDao.updateOne(Criteria.where("billNo").is(billNo), new Document("status", BillStatus.Save.toString()));
            throw e;
        }
        return null;
    }


    @Override
    public VtuzxMap getBatchAndExpMaterialTempList(ObjectId userId, String name) {
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> batchAndExpDetailItr = batchAndExpDetailDao.findList(Criteria.where("_createUserId").is(userId).and("isTemp").is(true), null, 0, 0);
        while (batchAndExpDetailItr.hasNext()) {
            Document batchAndExpDetailDoc = batchAndExpDetailItr.next();
            VtuzxMap data = new VtuzxMap(batchAndExpDetailDoc);
            data.append("id", VtuzxUtil.removeString(data, IVtuzxConst.Key__id));
            data.append("createUserId", VtuzxUtil.removeString(data, "_createUserId"));
            data.append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.removeObject(data, "createTime")));
            data.remove("updateTime");
            dataList.add(data);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }


    @Override
    public VtuzxMap getBatchAndExpMaterialTempBoxList(ObjectId userId, String name, String id) {
        List<VtuzxMap> dataList = new ArrayList<>();
        Iterator<Document> boxItr = batchAndExpDetailBoxDao.findList(Criteria.where("_detailId").is(FastUtil.convertId(id)), null, 0, 0);
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            VtuzxMap data = new VtuzxMap(boxDoc);
            data.append("id", VtuzxUtil.removeString(data, IVtuzxConst.Key__id));
            data.append("createUserId", VtuzxUtil.removeString(data, "_createUserId"));
            data.append("detailId", VtuzxUtil.removeString(data, "_detailId"));
            data.append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.removeObject(data, "createTime")));
            data.remove("updateTime");
            dataList.add(data);
        }
        return new VtuzxMap(IVtuzxConst.Key_data, dataList);
    }


    @Override
    public void deleteBillBatchAndExp(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        Criteria cond = new Criteria("billNo").is(billNo);
        batchAndExpDao.deleteOne(cond);
        batchAndExpDetailDao.deleteMany(cond);
        batchAndExpDetailBoxDao.deleteMany(cond);
    }



    @Override
    public void deleteMaterialBatchAndExp(ObjectId userId, String name, Map<String, Object> param) {
        String id = VtuzxUtil.getString(param, "id");
        batchAndExpDetailDao.deleteById(id);
        batchAndExpDetailBoxDao.deleteMany(Criteria.where("_detailId").is(new ObjectId(id)));

    }

    @Override
    public void deleteBoxBatchAndExp(ObjectId userId, String name, Map<String, Object> param) {
        String id = VtuzxUtil.getString(param, "id");
        Document box = batchAndExpDetailBoxDao.findById(id);
        ObjectId _detailId = VtuzxUtil.getObject(box, "_detailId");
        batchAndExpDetailBoxDao.deleteById(id);
        double count = 0d;
        int boxCount = 0;
        Iterator<Document> boxItr = batchAndExpDetailBoxDao.findList(Criteria.where("_detailId").is(_detailId), null, 0, 0);
        while (boxItr.hasNext()) {
            Document boxDoc = boxItr.next();
            count += VtuzxUtil.getDouble(boxDoc, "count");
            boxCount ++;
        }
        if (boxCount == 0) {
            batchAndExpDetailDao.deleteById(_detailId);
        } else {
            batchAndExpDetailDao.updateById(_detailId, new Document("count", count).append("boxCount", boxCount));
        }
    }


    @Override
    public VtuzxMap getBatchAndExpList(ObjectId userId, String name, Map<String, Object> param) {
        String billNo = VtuzxUtil.getString(param, "billNo");
        String fromBatchNo = VtuzxUtil.getString(param, "fromBatchNo");
        String batchNo = VtuzxUtil.getString(param, "batchNo");
        String createUserName = VtuzxUtil.getString(param, "createUserName");
        String auditUserName = VtuzxUtil.getString(param, "auditUserName");
        int pageIndex = VtuzxUtil.getInt(param, "pageIndex");
        int pageSize = VtuzxUtil.getInt(param, "pageSize");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(billNo)) {
            cond.and("billNo").regex(billNo);
        }
        if (!VtuzxUtil.isEmpty(batchNo)) {
            cond.and("batchNo").regex(batchNo);
        }
        if (!VtuzxUtil.isEmpty(fromBatchNo)) {
            cond.and("fromBatchNo").regex(fromBatchNo);
        }
        if (!VtuzxUtil.isEmpty(createUserName)) {
            cond.and("createUserName").regex(createUserName);
        }
        if (!VtuzxUtil.isEmpty(auditUserName)) {
            cond.and("auditUserName").regex(auditUserName);
        }
        VtuzxPage page = batchAndExpDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("id", VtuzxUtil.getString(doc, IVtuzxConst.Key__id))
                .append("billNo", VtuzxUtil.getString(doc, "billNo"))
                .append("fromBatchNo", VtuzxUtil.getString(doc, "fromBatchNo"))
                .append("fromExp", VtuzxUtil.getInt(doc, "fromExp"))
                .append("batchNo", VtuzxUtil.getString(doc, "batchNo"))
                .append("exp", VtuzxUtil.getInt(doc, "exp"))
                .append("createUserId", VtuzxUtil.getString(doc, "_createUserId"))
                .append("createUserName", VtuzxUtil.getString(doc, "createUserName"))
                .append("_auditUserId", VtuzxUtil.getString(doc, "_auditUserId"))
                .append("status", VtuzxUtil.getString(doc, "status"))
                .append("auditUserName", VtuzxUtil.getString(doc, "auditUserName"))
                .append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(doc, "createTime")))
                .append("auditTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(doc, "auditTime"))));
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }


    @Override
    public VtuzxMap getBatchAndExpData(ObjectId userId, String name, String billNo) {
        VtuzxMap header = new VtuzxMap();
        List<VtuzxMap> dataList = new ArrayList<>();

        Document batchAndExpDoc = batchAndExpDao.findOne(Criteria.where("billNo").is(billNo));
        String batchNo = VtuzxUtil.getString(batchAndExpDoc, "batchNo");
        String status = VtuzxUtil.getString(batchAndExpDoc, "status");
        int exp = VtuzxUtil.getInt(batchAndExpDoc, "exp");
        header.append("billNo", billNo)
                .append("updateBatchNo", batchNo)
                .append("updateExp", exp)
                .append("status", status);
        Iterator<Document> batchAndExpDetailItr = batchAndExpDetailDao.findList(Criteria.where("billNo").is(billNo), new Document("createTime", -1), 0, 0);
        while (batchAndExpDetailItr.hasNext()) {
            Document batchAndExpDetailDoc = batchAndExpDetailItr.next();
            VtuzxMap batchAndExpDetailMap = new VtuzxMap(batchAndExpDetailDoc);
            batchAndExpDetailMap.append("id", VtuzxUtil.removeString(batchAndExpDetailMap, "_id"));
            dataList.add(batchAndExpDetailMap);
        }

        return new VtuzxMap("header", header).append("dataList", dataList);
    }

    @Override
    public VtuzxMap getInventoryBatch(ObjectId userId, String name, String key) {
        List<String> batchNoList = new ArrayList<>();
        Iterator<Document> inventoryItr = inventoryDao.findList(Criteria.where("boxCount").gt(0).and("batchNo").regex("^" + key + ".*$"), null, new Document("batchNo", 1).append(IVtuzxConst.Key__id, 0), 0, 500);
        while(inventoryItr.hasNext()) {
            Document doc = inventoryItr.next();
            String batchNo = VtuzxUtil.getString(doc, "batchNo");
            if (!batchNoList.contains(batchNo)) {
                batchNoList.add(batchNo);
            }
            if (batchNoList.size() == 20) {
                break;
            }
        }
        return new VtuzxMap(IVtuzxConst.Key_data, batchNoList);
    }

    @Override
    public VtuzxMap getInventoryBaocai(ObjectId userId, String name, String boxNoKey, String stockNo) {
        String boxNo = "";
        List<VtuzxMap> dataList = new ArrayList<>();
        if (boxNoKey.contains("|;")) {
            String[] boxValues = boxNoKey.split("\\|\\;");
            if (boxValues.length == 5) {
                boxNo = boxNoKey;
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo.substring(0, boxNo.lastIndexOf("|;"));
                boxNo = boxNo + "|;batch";
            }
            Criteria cond = Criteria.where("boxNo").is(boxNo).and("status").is("in").and("stockNo").is(stockNo);

            List<String> locationList = new ArrayList<>();
            Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(cond, null, 0, 0);
            while (inventoryDetailItr.hasNext()) {
                Document inventoryDetail = inventoryDetailItr.next();
                String locationNo = VtuzxUtil.getString(inventoryDetail, "locationNo");
                locationList.add(locationNo);
            }

            Iterator<Document> locationItr = stockDetailDao.findList(Criteria.where("locationNo").in(locationList), null, 0, 0);
            while (locationItr.hasNext()) {
                Document locationDoc = locationItr.next();
                String locationNo = VtuzxUtil.getString(locationDoc, "locationNo");
                String locationName = VtuzxUtil.getString(locationDoc, "locationName");
                VtuzxMap map = new VtuzxMap();
                map.append("locationNo", locationNo).append("locationName", locationName);
                dataList.add(map);
            }

            if (dataList.size() == 1) {

            } else if (dataList.size() > 1) {
                return new VtuzxMap(IVtuzxConst.Key_data, dataList);
            }
        }
        return null;
    }

    /**
     * 物料收发汇总报表
     * @param userId
     * @param name
     * @param param
     * @return
     */
    @Override
    public VtuzxMap getMaterialSummaryList(ObjectId userId, String name, Map<String, Object> param) {
        String fMaterialNo = VtuzxUtil.getString(param,"materialNo");
        String fStockNo = VtuzxUtil.getString(param,"stockNo");
        String batchMaterialNo = VtuzxUtil.getString(param,"batchMaterialNo");
        String fbatchNo = VtuzxUtil.getString(param,"batchNo");
        List<String>  fCreateTime = VtuzxUtil.getObject(param, "createTime");
        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        Criteria cond = new Criteria();
        Criteria materialCond = new Criteria();
        Criteria desaltingCond = new Criteria("desalting").is(true);//生产变品入库
        Criteria returnCond = new Criteria("return").is(true);//退料入库
        Criteria inCond = new Criteria("task").is(true);//生产入库
        if (!VtuzxUtil.isEmpty(fMaterialNo)) {
            materialCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(batchMaterialNo)){
            materialCond.and("materialNo").is(batchMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(fStockNo)) {
            materialCond.and("stockNo").is(fStockNo);
        }
        if (!VtuzxUtil.isEmpty(fbatchNo)){
            materialCond.and("batchNo").regex(fbatchNo);
        }
        if (!VtuzxUtil.isEmpty(fCreateTime)) {
            materialCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            desaltingCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            returnCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            cond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            inCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));

        }

        long total = inventoryDao.countAllMaterial(materialCond);
        //存放仓库编号
        List<String> stockNoList = new ArrayList<>();
        //存物料编号
        List<String> materialNoList = new ArrayList<>();
        List<String> batchNoList = new ArrayList<>();
        //new Document("_id.stockNo", 1).append("_id.materialNo", 1)
        //用库存表做主表 分页  去重
        Iterator<Document> itr = inventoryDao.findAllMaterial(materialCond,new Document("createTime",1), pageIndex, pageSize);
        //返回的数据集合
        List<VtuzxMap> resultList = new ArrayList<>();
        while(itr.hasNext()) {
            Document doc = itr.next();
            Document idDoc = VtuzxUtil.getObject(doc, IVtuzxConst.Key__id);
            String stockNo = VtuzxUtil.getString(idDoc, "stockNo");
            String materialNo = VtuzxUtil.getString(idDoc, "materialNo");
            String batchNo = VtuzxUtil.getString(idDoc,"batchNo");
            if (!stockNoList.contains(stockNo)){
                stockNoList.add(stockNo);
            }
            if (!materialNoList.contains(materialNo)){
                materialNoList.add(materialNo);
            }
            if (!batchNoList.contains(batchNo)){
                batchNoList.add(batchNo);
            }
            VtuzxMap data = new VtuzxMap("stockNo", stockNo)
                    .append("batchNo",batchNo)
                    .append("materialNo", materialNo);
            resultList.add(data);
        }
        desaltingCond.and("materialNo").in(materialNoList)
                .and("stockNo").in(stockNoList)
                .and("batchNo").in(batchNoList);
        returnCond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);
        cond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);
        inCond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);


        List<VtuzxMap> allList = new ArrayList<>();//所有出库单的总和
        List<VtuzxMap> allEnteryList = new ArrayList<>();//所有入库单的总和
        //其他入库
        Iterator<Document> otherInDetailItr  = otherInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> otherInList = changeIterator(otherInDetailItr);
        //退料入库
        Iterator<Document> prdReturnDetailItr  = prdInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> prdReturnList = changeIterator(prdReturnDetailItr);
//        //生产变品入库
//        Iterator<Document> prdDesDetailItr  = prdInDetailDao.findList(desaltingCond,null,0,0);
//        List<VtuzxMap> prdDesList = changeIterator(prdDesDetailItr);
//        //生产入库单
//        Iterator<Document> purInEnterDetailItr  = prdInDetailDao.findList(inCond,null,0,0);
//        List<VtuzxMap> prdInList = changeIterator(purInEnterDetailItr);
        //采购入库单据
        Iterator<Document> purInDetailItr  = purInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> purInList = changeIterator(purInDetailItr);
        //生产领料单单据
        Iterator<Document> prdPickDetailItr = materialBomPickDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> prdPickList = changeIterator(prdPickDetailItr);
        //其他出库单据
        Iterator<Document>  otherOutDetailItr = otherOutDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> otherOutList = changeIterator(otherOutDetailItr);
        //销售出库单据
//        Iterator<Document> saleOutDetailItr = saleOutDetailDao.findList(cond,null,0,0);
//        List<VtuzxMap> saleOutList = changeIterator(saleOutDetailItr);
        //调拨单入库数据
        Iterator<Document> allocationDetailItr = allocationDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> allocationList = changeIterator(allocationDetailItr);

        //调拨单出库数据 先从detaildao里取出调拨单号
        Iterator<Document> outAllocationItr = allocationDetailDao.findList(cond,null,0,0);
        List<String> billNoList = new ArrayList<>();//存放调拨单号
        List<VtuzxMap> billList = new ArrayList<>();
        while (outAllocationItr.hasNext()){
            Document allocationDoc = outAllocationItr.next();
            String materialNo = VtuzxUtil.getString(allocationDoc,"materialNo");
            String billNo = VtuzxUtil.getString(allocationDoc,"billNo");
            String batchNo = VtuzxUtil.getString(allocationDoc,"batchNo");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("materialNo",materialNo)
                    .append("batchNo",batchNo)
                    .append("billNo",billNo);
            if (!billNoList.contains(billNo)){
                billNoList.add(billNo);
            }
            billList.add(dataMap);
        }

        //根据调拨号从调拨表里取出原仓库信息
        Iterator<Document> allocationOutItr = allocationDao.findList(Criteria.where("billNo").in(billNoList),null,0,0);
        List<VtuzxMap> allocationOutList = new ArrayList<>();
        while (allocationOutItr.hasNext()){
            Document allocationOutDoc = allocationOutItr.next();
            String billNo = VtuzxUtil.getString(allocationOutDoc,"billNo");
            String fromStockNo = VtuzxUtil.getString(allocationOutDoc,"fromStockNo");
            double count = VtuzxUtil.getDouble(allocationOutDoc,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("billNo",billNo)
                    .append("stockNo",fromStockNo)
                    .append("count",count);
            allocationOutList.add(dataMap);
        }
        //把物料编号添加到原仓库里
        for (VtuzxMap outData:allocationOutList){
            String fBillNo = VtuzxUtil.getString(outData,"billNo");
            for (VtuzxMap data:billList){
                String billNo = VtuzxUtil.getString(data,"billNo");
                String materialNo = VtuzxUtil.getString(data,"materialNo");
                if (fBillNo.equals(billNo)){
                    outData.append("materialNo",materialNo);
                }
            }
        }



        //把其他出库单放在所有出库单总和里
        allList.addAll(prdPickList);
        allList.addAll(otherOutList);
        allList.addAll(allocationOutList);
//        allList.addAll(saleOutList);

        //把其他入库单放在所有入库单总和里
        allEnteryList.addAll(purInList);
//        allEnteryList.addAll(prdInList);
//        allEnteryList.addAll(prdDesList);
        allEnteryList.addAll(prdReturnList);
        allEnteryList.addAll(otherInList);
        allEnteryList.addAll(allocationList);
        Map<String,Object> inMap = new HashMap<>();
        for (VtuzxMap entry:allEnteryList){
            String materialNo = VtuzxUtil.getString(entry,"materialNo");
            String stockNo = VtuzxUtil.getString(entry,"stockNo");
            String batchNo = VtuzxUtil.getString(entry,"batchNo");
            double count = VtuzxUtil.getDouble(entry,"count");
            double mapCount = VtuzxUtil.getDouble(inMap,materialNo + batchNo + stockNo);
            if(!VtuzxUtil.isEmpty(mapCount)){
                inMap.put(materialNo + batchNo + stockNo,count + mapCount);
            } else {
                inMap.put(materialNo + batchNo + stockNo,count );
            }

        }

        Map<String,Object> outMap = new HashMap<>();
        for (VtuzxMap entry:allList){
            String materialNo = VtuzxUtil.getString(entry,"materialNo");
            String stockNo = VtuzxUtil.getString(entry,"stockNo");
            String batchNo = VtuzxUtil.getString(entry,"batchNo");
            double count = VtuzxUtil.getDouble(entry,"count");
            double mapCount = VtuzxUtil.getDouble(outMap,materialNo + batchNo + stockNo);
            if(!VtuzxUtil.isEmpty(mapCount)){
                outMap.put(materialNo + batchNo + stockNo,count + mapCount);
            } else {
                outMap.put(materialNo + batchNo + stockNo,count );
            }
        }


        //计算收入数量
        for (VtuzxMap res:resultList){
            String fNumber = VtuzxUtil.getString(res,"materialNo");
            String inStockNo = VtuzxUtil.getString(res,"stockNo");
            String resBatchNo = VtuzxUtil.getString(res,"batchNo");
            double num = VtuzxUtil.getDouble(inMap,fNumber + resBatchNo + inStockNo);
            String matNum = String.format("%.2f",num);
            res.append("incomeNum",matNum);
        }

        //计算发出数量
        for (VtuzxMap res:resultList){
            String fNumber = VtuzxUtil.getString(res,"materialNo");
            String outStockNo = VtuzxUtil.getString(res,"stockNo");
            String outBatch = VtuzxUtil.getString(res,"batchNo");
            double num =VtuzxUtil.getDouble(outMap,fNumber + outBatch + outStockNo);
            String formatCount = String.format("%.2f",num);
            res.append("issueNum",formatCount);
        }

        //去仓库表查询仓库名称
        Map<String,Object> stockMap = new HashMap<>();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockNoList),null,0,0);
        while (stockItr.hasNext()){
            Document stockDoc = stockItr.next();
            String stockNo = VtuzxUtil.getString(stockDoc,"fNumber");
            String stockName = VtuzxUtil.getString(stockDoc,"fName");
           stockMap.put(stockNo,stockName);
        }
        //把仓库名放到resultlist
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "stockNo");
            String stockName = stockMap.get(fNumber).toString();
            data.append("stockName",stockName);
        }

        //去物料表查询物料名
        Map<String,Object> materialMap = new HashMap<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            String materialNo = VtuzxUtil.getString(materialDoc,"fNumber");
            String materialName = VtuzxUtil.getString(materialDoc,"fName");
            String unit = VtuzxUtil.getString(materialDoc,"fBaseUnitIdFName");
            materialMap.put(materialNo,materialName + "," + unit);
        }

        //把物料名称添加上
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "materialNo");
            String material = materialMap.get(fNumber).toString();
            String materialName = material.substring(0,material.indexOf(","));
            String unit = material.substring(material.indexOf(",") + 1);
            data.append("materialName",materialName)
                .append("unit",unit);
        }

        //计算期入
        if (fCreateTime == null || VtuzxUtil.isEmpty(fCreateTime)){
            for (VtuzxMap data:resultList){
                data.append("opening",0);
            }
        } else {
            for (VtuzxMap data:resultList){
                String materialNo = VtuzxUtil.getString(data,"materialNo");
                String stockNo = VtuzxUtil.getString(data,"stockNo");
                String batchNo = VtuzxUtil.getString(data,"batchNo");
                double opening =  getOneOpening(materialNo,stockNo,fCreateTime.get(0),batchNo);
                String matOpening = String.format("%.2f",opening);
                data.append("opening",matOpening);
            }
        }

        //计算结存
        for (VtuzxMap res:resultList){
            double opening = VtuzxUtil.getDouble(res,"opening");
            double incomeNum = VtuzxUtil.getDouble(res,"incomeNum");
            double issueNum = VtuzxUtil.getDouble(res,"issueNum");
            double balance = opening + incomeNum -issueNum;
            String forBalance = String.format("%.2f",balance);
            res.append("balance",forBalance);
        }

        return new VtuzxMap(IVtuzxConst.Key_total, total).append(IVtuzxConst.Key_data, resultList);
    }



    //把Iterator转换成List
    @Override
    public List<VtuzxMap> changeIterator(Iterator<Document> innerIterator) {
        List<VtuzxMap> innerList = new ArrayList<>();
        while (innerIterator.hasNext()){
            Document innerDoc = innerIterator.next();
            String materialNo = VtuzxUtil.getString(innerDoc,"materialNo");
            String stockNo = VtuzxUtil.getString(innerDoc,"stockNo");
            String batchNo = VtuzxUtil.getString(innerDoc,"batchNo");
            double count = VtuzxUtil.getDouble(innerDoc,"count");
            VtuzxMap vtuzxMap = new VtuzxMap();
            vtuzxMap.append("materialNo", materialNo)
                    .append("stockNo", stockNo)
                    .append("batchNo",batchNo)
                    .append("count", count);
            innerList.add(vtuzxMap);
        }
        return innerList;
    }

    @Override
    public VtuzxDownloadBean downloadMaterialSummary(VtuzxToken token, Map<String, Object> param) throws Exception {
        String fMaterialNo = VtuzxUtil.getString(param,"materialNo");
        String fStockNo = VtuzxUtil.getString(param,"stockNo");
        String batchMaterialNo = VtuzxUtil.getString(param,"batchMaterialNo");
        String fbatchNo = VtuzxUtil.getString(param,"batchNo");
        List<String>  fCreateTime = VtuzxUtil.getObject(param, "createTime");
        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        Criteria cond = new Criteria();
        Criteria materialCond = new Criteria();
        Criteria desaltingCond = new Criteria("desalting").is(true);//生产变品入库
        Criteria returnCond = new Criteria("return").is(true);//退料入库
        Criteria inCond = new Criteria("task").is(true);//生产入库
        if (!VtuzxUtil.isEmpty(fMaterialNo)) {
            materialCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(batchMaterialNo)){
            materialCond.and("materialNo").is(batchMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(fStockNo)) {
            materialCond.and("stockNo").is(fStockNo);
        }
        if (!VtuzxUtil.isEmpty(fbatchNo)){
            materialCond.and("batchNo").regex(fbatchNo);
        }
        if (!VtuzxUtil.isEmpty(fCreateTime)) {
            materialCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            desaltingCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            returnCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            cond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));
            inCond.and("createTime").gte(VtuzxUtil.parseDate(fCreateTime.get(0))).lte(VtuzxUtil.parseDate(fCreateTime.get(1)));

        }

        long total = inventoryDao.countAllMaterial(materialCond);
        //存放仓库编号
        List<String> stockNoList = new ArrayList<>();
        //存物料编号
        List<String> materialNoList = new ArrayList<>();
        List<String> batchNoList = new ArrayList<>();
        //new Document("_id.stockNo", 1).append("_id.materialNo", 1)
        //用库存表做主表 分页  去重
        Iterator<Document> itr = inventoryDao.findAllMaterial(materialCond,new Document("createTime",1), pageIndex, pageSize);
        //返回的数据集合
        List<VtuzxMap> resultList = new ArrayList<>();
        while(itr.hasNext()) {
            Document doc = itr.next();
            Document idDoc = VtuzxUtil.getObject(doc, IVtuzxConst.Key__id);
            String stockNo = VtuzxUtil.getString(idDoc, "stockNo");
            String materialNo = VtuzxUtil.getString(idDoc, "materialNo");
            String batchNo = VtuzxUtil.getString(idDoc,"batchNo");
            if (!stockNoList.contains(stockNo)){
                stockNoList.add(stockNo);
            }
            if (!materialNoList.contains(materialNo)){
                materialNoList.add(materialNo);
            }
            if (!batchNoList.contains(batchNo)){
                batchNoList.add(batchNo);
            }
            VtuzxMap data = new VtuzxMap("stockNo", stockNo)
                    .append("batchNo",batchNo)
                    .append("materialNo", materialNo);
            resultList.add(data);
        }
        desaltingCond.and("materialNo").in(materialNoList)
                .and("stockNo").in(stockNoList)
                .and("batchNo").in(batchNoList);
        returnCond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);
        cond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);
        inCond.and("materialNo").in(materialNoList).and("stockNo").in(stockNoList).and("batchNo").in(batchNoList);


        List<VtuzxMap> allList = new ArrayList<>();//所有出库单的总和
        List<VtuzxMap> allEnteryList = new ArrayList<>();//所有入库单的总和
        //其他入库
        Iterator<Document> otherInDetailItr  = otherInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> otherInList = changeIterator(otherInDetailItr);
        //退料入库
        Iterator<Document> prdReturnDetailItr  = prdInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> prdReturnList = changeIterator(prdReturnDetailItr);
//        //生产变品入库
//        Iterator<Document> prdDesDetailItr  = prdInDetailDao.findList(desaltingCond,null,0,0);
//        List<VtuzxMap> prdDesList = changeIterator(prdDesDetailItr);
//        //生产入库单
//        Iterator<Document> purInEnterDetailItr  = prdInDetailDao.findList(inCond,null,0,0);
//        List<VtuzxMap> prdInList = changeIterator(purInEnterDetailItr);
        //采购入库单据
        Iterator<Document> purInDetailItr  = purInDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> purInList = changeIterator(purInDetailItr);
        //生产领料单单据
        Iterator<Document> prdPickDetailItr = materialBomPickDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> prdPickList = changeIterator(prdPickDetailItr);
        //其他出库单据
        Iterator<Document>  otherOutDetailItr = otherOutDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> otherOutList = changeIterator(otherOutDetailItr);
        //销售出库单据
//        Iterator<Document> saleOutDetailItr = saleOutDetailDao.findList(cond,null,0,0);
//        List<VtuzxMap> saleOutList = changeIterator(saleOutDetailItr);
        //调拨单入库数据
        Iterator<Document> allocationDetailItr = allocationDetailDao.findList(cond,null,0,0);
        List<VtuzxMap> allocationList = changeIterator(allocationDetailItr);

        //调拨单出库数据 先从detaildao里取出调拨单号
        Iterator<Document> outAllocationItr = allocationDetailDao.findList(cond,null,0,0);
        List<String> billNoList = new ArrayList<>();//存放调拨单号
        Map<String,Object> allocationMap = new HashMap<>();
        List<VtuzxMap> billList = new ArrayList<>();
        while (outAllocationItr.hasNext()){
            Document allocationDoc = outAllocationItr.next();
            String materialNo = VtuzxUtil.getString(allocationDoc,"materialNo");
            String billNo = VtuzxUtil.getString(allocationDoc,"billNo");
            String batchNo = VtuzxUtil.getString(allocationDoc,"batchNo");
            if (!billNoList.contains(billNo)){
                billNoList.add(billNo);
            }
            allocationMap.put(billNo,materialNo);
        }

        //根据调拨号从调拨表里取出原仓库信息
        Iterator<Document> allocationOutItr = allocationDao.findList(Criteria.where("billNo").in(billNoList),null,0,0);
        List<VtuzxMap> allocationOutList = new ArrayList<>();
        while (allocationOutItr.hasNext()){
            Document allocationOutDoc = allocationOutItr.next();
            String billNo = VtuzxUtil.getString(allocationOutDoc,"billNo");
            String fromStockNo = VtuzxUtil.getString(allocationOutDoc,"fromStockNo");
            double count = VtuzxUtil.getDouble(allocationOutDoc,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("billNo",billNo)
                    .append("stockNo",fromStockNo)
                    .append("count",count);
            allocationOutList.add(dataMap);
        }
        //把物料编号添加到原仓库里
        for (VtuzxMap outData:allocationOutList){
            String fBillNo = VtuzxUtil.getString(outData,"billNo");
            String materialNo = allocationMap.get(fBillNo).toString();
            outData.append("materialNo",materialNo);

        }



        //把其他出库单放在所有出库单总和里
        allList.addAll(prdPickList);
        allList.addAll(otherOutList);
        allList.addAll(allocationOutList);
//        allList.addAll(saleOutList);

        //把其他入库单放在所有入库单总和里
        allEnteryList.addAll(purInList);
//        allEnteryList.addAll(prdInList);
//        allEnteryList.addAll(prdDesList);
        allEnteryList.addAll(prdReturnList);
        allEnteryList.addAll(otherInList);
        allEnteryList.addAll(allocationList);
        Map<String,Object> inMap = new HashMap<>();
        for (VtuzxMap entry:allEnteryList){
            String materialNo = VtuzxUtil.getString(entry,"materialNo");
            String stockNo = VtuzxUtil.getString(entry,"stockNo");
            String batchNo = VtuzxUtil.getString(entry,"batchNo");
            double count = VtuzxUtil.getDouble(entry,"count");
            double mapCount = VtuzxUtil.getDouble(inMap,materialNo + batchNo + stockNo);
            if(!VtuzxUtil.isEmpty(mapCount)){
                inMap.put(materialNo + batchNo + stockNo,count + mapCount);
            } else {
                inMap.put(materialNo + batchNo + stockNo,count );
            }

        }

        Map<String,Object> outMap = new HashMap<>();
        for (VtuzxMap entry:allList){
            String materialNo = VtuzxUtil.getString(entry,"materialNo");
            String stockNo = VtuzxUtil.getString(entry,"stockNo");
            String batchNo = VtuzxUtil.getString(entry,"batchNo");
            double count = VtuzxUtil.getDouble(entry,"count");
            double mapCount = VtuzxUtil.getDouble(outMap,materialNo + batchNo + stockNo);
            if(!VtuzxUtil.isEmpty(mapCount)){
                outMap.put(materialNo + batchNo + stockNo,count + mapCount);
            } else {
                outMap.put(materialNo + batchNo + stockNo,count );
            }
        }


        //计算收入数量
        for (VtuzxMap res:resultList){
            String fNumber = VtuzxUtil.getString(res,"materialNo");
            String inStockNo = VtuzxUtil.getString(res,"stockNo");
            String resBatchNo = VtuzxUtil.getString(res,"batchNo");
            double num = VtuzxUtil.getDouble(inMap,fNumber + resBatchNo + inStockNo);
            String matNum = String.format("%.2f",num);
            res.append("incomeNum",matNum);
        }

        //计算发出数量
        for (VtuzxMap res:resultList){
            String fNumber = VtuzxUtil.getString(res,"materialNo");
            String outStockNo = VtuzxUtil.getString(res,"stockNo");
            String outBatch = VtuzxUtil.getString(res,"batchNo");
            double num =VtuzxUtil.getDouble(outMap,fNumber + outBatch + outStockNo);
            String formatCount = String.format("%.2f",num);
            res.append("issueNum",formatCount);
        }

        //去仓库表查询仓库名称
        Map<String,Object> stockMap = new HashMap<>();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockNoList),null,0,0);
        while (stockItr.hasNext()){
            Document stockDoc = stockItr.next();
            String stockNo = VtuzxUtil.getString(stockDoc,"fNumber");
            String stockName = VtuzxUtil.getString(stockDoc,"fName");
            stockMap.put(stockNo,stockName);
        }
        //把仓库名放到resultlist
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "stockNo");
            String stockName = stockMap.get(fNumber).toString();
            data.append("stockName",stockName);
        }

        //去物料表查询物料名
        Map<String,Object> materialMap = new HashMap<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            String materialNo = VtuzxUtil.getString(materialDoc,"fNumber");
            String materialName = VtuzxUtil.getString(materialDoc,"fName");
            String unit = VtuzxUtil.getString(materialDoc,"fBaseUnitIdFName");
            materialMap.put(materialNo,materialName + "," + unit);
        }

        //把物料名称添加上
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "materialNo");
            String material = materialMap.get(fNumber).toString();
            String materialName = material.substring(0,material.indexOf(","));
            String unit = material.substring(material.indexOf(",") + 1);
            data.append("materialName",materialName)
                    .append("unit",unit);
        }

        //计算期入
//        if (fCreateTime == null || VtuzxUtil.isEmpty(fCreateTime)){
//            for (VtuzxMap data:resultList){
//                data.append("opening",0);
//            }
//        } else {
//            for (VtuzxMap data:resultList){
//                String materialNo = VtuzxUtil.getString(data,"materialNo");
//                String stockNo = VtuzxUtil.getString(data,"stockNo");
//                String batchNo = VtuzxUtil.getString(data,"batchNo");
//                double opening =  getOneOpening(materialNo,stockNo,fCreateTime.get(0),batchNo);
//                String matOpening = String.format("%.2f",opening);
//                data.append("opening",matOpening);
//            }
//        }

        //计算结存
        for (VtuzxMap res:resultList){
            double opening = VtuzxUtil.getDouble(res,"opening");
            double incomeNum = VtuzxUtil.getDouble(res,"incomeNum");
            double issueNum = VtuzxUtil.getDouble(res,"issueNum");
            double balance = opening + incomeNum -issueNum;
            String forBalance = String.format("%.2f",balance);
            res.append("balance",forBalance);
        }



        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "物料台账");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "物料编号"),
                new VtuzxExcelCell(col++, "批次"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "仓库名称"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "期入"),
                new VtuzxExcelCell(col++, "收入"),
                new VtuzxExcelCell(col++, "发出"),
                new VtuzxExcelCell(col++, "结存")

        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : resultList) {

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "unit")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "opening")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "incomeNum")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "issueNum")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "balance"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 200},
                new int[]{4, 150},
                new int[]{5, 150},
                new int[]{6, 150},
                new int[]{7, 150},
                new int[]{8, 150},
                new int[]{9, 150}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("物料台账" + timestamp + ".xlsx", stream);
    }




    /**
     * 物料收发明细
     * @param userId
     * @param name
     * @param param
     * @return
     */
    @Override
    public VtuzxMap getMaterialSummaryDetailList(ObjectId userId, String name, Map<String, Object> param) {
        String fMaterialNo = VtuzxUtil.getString(param,"materialNo");
        String fStockNo = VtuzxUtil.getString(param,"stockNo");
        String fBillNo = VtuzxUtil.getString(param,"billNo");
        String fBatchNo = VtuzxUtil.getString(param,"batchNo");
        String batchMaterialNo = VtuzxUtil.getString(param,"batchMaterialNo");
        List<String>  fcreateTime = VtuzxUtil.getObject(param, "createTime");
        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        Criteria cond = new Criteria();
        cond.and("status").ne("save");
        Criteria testCond = new Criteria();
        testCond.and("status").ne("save");
        //为了计算批次调整单的数据，在加两个条件
        Criteria batchCond = new Criteria();
        batchCond.and("status").ne("save");
        Criteria batchCondOr = new Criteria();
        batchCondOr.and("status").ne("save");

        Criteria allCond = new Criteria();
        if (!VtuzxUtil.isEmpty(fMaterialNo)) {
            cond.and("materialNo").is(fMaterialNo);
            testCond.and("materialNo").is(fMaterialNo);
            batchCond.and("materialNo").is(fMaterialNo);
            batchCondOr.and("materialNo").is(fMaterialNo);
            allCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(batchMaterialNo)) {
            cond.and("materialNo").is(fMaterialNo);
            testCond.and("materialNo").is(fMaterialNo);
            batchCond.and("materialNo").is(fMaterialNo);
            batchCondOr.and("materialNo").is(fMaterialNo);
            allCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(fBillNo)) {
            cond.and("billNo").regex(fBillNo);

        }
        if (!VtuzxUtil.isEmpty(fBatchNo)) {
            cond.and("batchNo").regex(fBatchNo);
            testCond.and("batchNo").regex(fBatchNo);
            batchCond.and("updateBatchNo").regex(fBatchNo);
            batchCondOr.and("updateBatchNo").regex(fBatchNo);
            allCond.and("batchNo").regex(fBatchNo);
        }
        if (!VtuzxUtil.isEmpty(fStockNo)) {
            cond.and("stockNo").is(fStockNo);
            testCond.and("toStockNo").is(fStockNo);
            batchCond.and("stockNo").is(fStockNo);
            batchCondOr.and("toStockNo").is(fStockNo);
            allCond.and("toStockNo").is(fStockNo);
        }
        if (!VtuzxUtil.isEmpty(fcreateTime)) {
            cond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            testCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            batchCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            batchCondOr.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            allCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
        }
        //查询总数
        long total = otherInDetailDao.countFindMaterialSummary(new Criteria().orOperator(testCond,cond,batchCond,batchCondOr));//计算总数
        List<String> materialNoList = new ArrayList<>();
        List<String> stockNoList = new ArrayList<>();
        List<String> batchNoList = new ArrayList<>();
        //或者条件查询,调拨单据特殊,还需判断调入仓库(toStockNo)
        Iterator<Document> itr = otherInDetailDao.findMaterialSummary(new Criteria().orOperator(cond,testCond,batchCond,batchCondOr),new Document("createTime",-1),pageIndex,pageSize);
        List<VtuzxMap> resultList = new ArrayList<>();
        while(itr.hasNext()) {
            Document v = itr.next();
            Object value = v.get("allValue");
            String billNo = VtuzxUtil.getString(v,"billNo");//单据号
            //存在没有单据编号但是有单据信息的情况,加一个判断
            if (!VtuzxUtil.isEmpty(billNo)){
                String materialNo = VtuzxUtil.getString(v,"materialNo");
                String batchNo = VtuzxUtil.getString(v,"batchNo");//批次
                String updateBatchNo = VtuzxUtil.getString(v,"updateBatchNo");//批次调整单修改后的批次
                String stockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
                String toStockNo = VtuzxUtil.getString(v,"toStockNo");//入库仓库编号
                double count = VtuzxUtil.getDouble(v,"count");
                if (!materialNoList.contains(materialNo)){
                    materialNoList.add(materialNo);
                }
                //如果批次的查询条件不为空的话
                if (!VtuzxUtil.isEmpty(fBatchNo)){
                    if(!VtuzxUtil.isEmpty(updateBatchNo)){//修改后批次也不为空说明是批次调整单
                        //需要判断是出库仓库还是入库仓库
                        if(batchNo.equals(fBatchNo)){
                            if(!batchNoList.contains(batchNo)){
                                batchNoList.add(batchNo);
                            }
                        } else if (updateBatchNo.equals(fBatchNo)){
                            if(!batchNoList.contains(batchNo)){
                                batchNoList.add(batchNo);
                            }
                        }
                    } else {//正常单据
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                    }
                } else {//如果批次查询条件为空的话
                    //判断是不是调拨单
                    if(!VtuzxUtil.isEmpty(batchNo) && !VtuzxUtil.isEmpty(updateBatchNo)){
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                        if(!batchNoList.contains(updateBatchNo)){
                            batchNoList.add(updateBatchNo);
                        }
                    } else {
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                    }
                }



                //如果仓库的查询条件不为空的话
                if (!VtuzxUtil.isEmpty(fStockNo)){
                    if(!VtuzxUtil.isEmpty(toStockNo)){//调入仓库也不为空说明是调拨单
                        //需要判断是出库仓库还是入库仓库
                        if(stockNo.equals(fStockNo)){
                            if(!stockNoList.contains(stockNo)){
                                stockNoList.add(stockNo);
                            }
                        } else if (toStockNo.equals(fStockNo)){
                            if(!stockNoList.contains(toStockNo)){
                                stockNoList.add(toStockNo);
                            }
                        }
                    } else {//正常单据
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    }
                } else {//如果仓库查询条件为空的话
                    //判断是不是调拨单
                    if(!VtuzxUtil.isEmpty(toStockNo) && !VtuzxUtil.isEmpty(stockNo)){
                        if(!stockNoList.contains(toStockNo)){
                            stockNoList.add(toStockNo);
                        }
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    } else {
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    }
                }

//                if (!batchNoList.contains(batchNo)){
//                    batchNoList.add(batchNo);
//                }

                VtuzxMap dataMap = new VtuzxMap();
                dataMap.append("materialNo",materialNo)
                        .append("batchNo",batchNo)
                        .append("billNo",billNo)
                        .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                        .append("auditTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                        .append("stockNo",stockNo);
                //issueNum : 发出数
                //incomeNum 入库数量
                if (billNo.startsWith("SCLL")){
                    dataMap.append("billName","生产领料单")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("QTCK")){
                    Document otherOutDoc = otherOutDao.findOne(Criteria.where("billNo").is(billNo));
                    dataMap.append("billName","其他出库单")
                            .append("auditTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(otherOutDoc,"auditTime")))
                            .append("issueNum",count);
                }
                if (billNo.startsWith("XSCK")){
                    dataMap.append("billName","销售出库")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("DBYK")){
                    //如果调出仓库和调入仓库都一样的话
                    if (stockNo.equals(fStockNo) && toStockNo.equals(fStockNo)){
                        dataMap.append("billName","直接调拨单")
                                .append("mark","mark")
                                .append("issueNum",count);
                        VtuzxMap inAllocation = new VtuzxMap();
                        inAllocation.append("materialNo",materialNo)
                                .append("batchNo",batchNo)
                                .append("billNo",billNo)
                                .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                .append("stockNo",toStockNo)
                                .append("mark","mark")
                                .append("billName","直接调拨单(入)")
                                .append("incomeNum",count);
                        resultList.add(inAllocation);
                        total += 1;
                    } else {//不一样的情况判断有没有仓库查询条件
                        if(!VtuzxUtil.isEmpty(fStockNo)){
                            if(stockNo.equals(fStockNo)){//调出仓库与查询的仓库相同
                                dataMap.append("billName","直接调拨单")
                                        .append("issueNum",count);
                                if (stockNo.equals(toStockNo)){
                                    dataMap.append("mark","mark");
                                }
                            } else if(toStockNo.equals(fStockNo)){
                                dataMap.append("billName","直接调拨单(入)")
                                        .append("stockNo",toStockNo)
                                        .append("incomeNum",count);
                                if (stockNo.equals(toStockNo)){
                                    dataMap.append("mark","mark");
                                }
                            }
                        } else {//如果仓库查询条件为空
                            dataMap.append("billName","直接调拨单")
                                    .append("issueNum",count);
                            VtuzxMap inAllocation = new VtuzxMap();
                            inAllocation.append("materialNo",materialNo)
                                    .append("batchNo",batchNo)
                                    .append("billNo",billNo)
                                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                    .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                    .append("stockNo",toStockNo)
                                    .append("billName","直接调拨单(入)")
                                    .append("incomeNum",count);
                            resultList.add(inAllocation);
                            total += 1;
                        }

                    }
                }
                if (billNo.startsWith("CGRK")){
                    dataMap.append("billName","采购入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("CJRK")){
                    dataMap.append("billName","生产入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("QTRK")){

                    dataMap.append("billName","其它入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("SCBL")){
                    dataMap.append("billName","生产补料")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("TZDD")){
                    if(!VtuzxUtil.isEmpty(fBatchNo)){
                        if(batchNo.equals(fBatchNo)){//前批次与查询的批次相同
                            dataMap.append("billName","批次调整单")
                                    .append("issueNum",count);
                        } else if(updateBatchNo.equals(fBatchNo)){
                            dataMap.append("billName","批次调整单(入)")
                                    .append("batchNo",updateBatchNo)
                                    .append("incomeNum",count);

                        }
                    } else {//如果批次查询条件为空
                        dataMap.append("billName","批次调整单")
                                .append("issueNum",count);
                        VtuzxMap inAllocation = new VtuzxMap();
                        inAllocation.append("materialNo",materialNo)
                                .append("batchNo",updateBatchNo)
                                .append("billNo",billNo)
                                .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                .append("stockNo",stockNo)
                                .append("billName","批次调整单(入)")
                                .append("incomeNum",count);
                        resultList.add(inAllocation);
                        total += 1;
                    }


                }
                resultList.add(dataMap);
            }

        }
        //去物料表查询物料名
        List<VtuzxMap> materList = new ArrayList<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            String materialNo = VtuzxUtil.getString(materialDoc,"fNumber");
            String materialName = VtuzxUtil.getString(materialDoc,"fName");
            String unit = VtuzxUtil.getString(materialDoc,"fBaseUnitIdFName");
            String fSpecification = VtuzxUtil.getString(materialDoc,"fSpecification");
            VtuzxMap materialMap = new VtuzxMap();
            materialMap.append("materialNo",materialNo)
                    .append("materialName",materialName)
                    .append("fSpecification",fSpecification)
                    .append("unit",unit);
            materList.add(materialMap);
        }
        //把物料名称添加上
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "materialNo");
            for (VtuzxMap matData : materList) {
                String materialNo = VtuzxUtil.getString(matData, "materialNo");
                String materialName = VtuzxUtil.getString(matData, "materialName");
                String unit = VtuzxUtil.getString(matData,"unit");
                String fSpecification = VtuzxUtil.getString(matData,"fSpecification");
                if (fNumber.equals(materialNo)) {
                    data.append("materialName",materialName)
                            .append("fSpecification",fSpecification)
                            .append("unit",unit);
                    continue;
                }
            }
        }

        Map<String,Object> stockMap = new HashMap<>();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockNoList),null,0,0);
        while (stockItr.hasNext()){
            Document stockDoc = stockItr.next();
            String stockNo = VtuzxUtil.getString(stockDoc,"fNumber");
            String stockName = VtuzxUtil.getString(stockDoc,"fName");
            stockMap.put(stockNo,stockName);
        }
        //把仓库名放到resultlist
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "stockNo");
            String stockName = VtuzxUtil.getString(stockMap,fNumber);
            data.append("stockName",stockName);
        }


        //计算期入
        List<VtuzxMap> inList = new ArrayList<>();//存放入库
        List<VtuzxMap> outList = new ArrayList<>();//存放出库
        Criteria inCond = new Criteria("materialNo").in(materialNoList).and("batchNo").in(batchNoList).and("toStockNo").in(stockNoList);
        Criteria inBatchCond = new Criteria("materialNo").in(materialNoList).and("updateBatchNo").in(batchNoList).and("toStockNo").in(stockNoList);
        Criteria inCondOr = new Criteria("materialNo").in(materialNoList).and("batchNo").in(batchNoList).and("stockNo").in(stockNoList);
        Criteria inBatchCondOr = new Criteria("materialNo").in(materialNoList).and("updateBatchNo").in(batchNoList).and("stockNo").in(stockNoList);
        //查询对应物料所有入和出数据(不区分时间)
        Iterator<Document> otherItr = otherInDetailDao.findInDetail(new Criteria().orOperator(inCond,inBatchCond,inCondOr,inBatchCondOr));
        while(otherItr.hasNext()) {
            Document v = otherItr.next();
            Object value = v.get("allValue");
            String materialNo = VtuzxUtil.getString(v,"materialNo");
            String stockNo = "";
            //String batchNo = VtuzxUtil.getString(v, "batchNo");
            String batchNo = "";
            String billNo = VtuzxUtil.getString(v, "billNo");
            if(billNo.startsWith("DBYK")){
                //如果是调拨单的话需要取入库仓库编号
                stockNo = VtuzxUtil.getString(v,"toStockNo");//仓库编号
            } else {
                stockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
            }
            if(billNo.startsWith("TZDD")){
                //如果是调拨单的话需要取入库仓库编号
                batchNo = VtuzxUtil.getString(v,"updateBatchNo");//仓库编号
            } else {
                batchNo = VtuzxUtil.getString(v,"batchNo");//仓库编号
            }
            double count = VtuzxUtil.getDouble(v,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("materialNo",materialNo)
                    .append("stockNo",stockNo)
                    .append("batchNo",batchNo)
                    .append("billNo",billNo)
                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                    .append("count",count);
            inList.add(dataMap);
        }
        //查询所有出库数据
        Criteria outCond = new Criteria("allValue.materialNo").in(materialNoList).and("allValue.batchNo").in(batchNoList).and("allValue.stockNo").in(stockNoList);
        outCond.and("allValue.status").ne("save");

        Criteria outCondOr = new Criteria("allValue.materialNo").in(materialNoList).and("allValue.updateBatchNo").in(batchNoList).and("allValue.stockNo").in(stockNoList);
        outCondOr.and("allValue.status").ne("save");
        Iterator<Document> outItr = materialBomPickDetailDao.findOutDetail(new Criteria().orOperator(outCond,outCondOr));
        while(outItr.hasNext()) {
            Document v = outItr.next();
            Object value = v.get("allValue");
            String materialNo = VtuzxUtil.getString((Map<String, Object>) value,"materialNo");
            String batchNo = VtuzxUtil.getString((Map<String, Object>) value,"batchNo");
            String stockNo = VtuzxUtil.getString((Map<String, Object>) value,"stockNo");//仓库编号
            //String createTime = VtuzxUtil.getString((Map<String, Object>) value,"createTime");
            double count = VtuzxUtil.getDouble((Map<String, Object>) value,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("materialNo",materialNo)
                    .append("stockNo",stockNo)
                    .append("batchNo",batchNo)
                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject((Map<String, Object>) value,"createTime")))
                    .append("count",count);
            outList.add(dataMap);
        }

        //计算入的数量
        for (VtuzxMap data:resultList){
            String materialNo = VtuzxUtil.getString(data,"materialNo");
            String stockNo = VtuzxUtil.getString(data,"stockNo");
            String createTime = VtuzxUtil.getString(data,"createTime");
            String batchNo = VtuzxUtil.getString(data,"batchNo");
            double opening = 0d;//期初数
            for (VtuzxMap dataMap: inList){
                String inMateriaLNo = VtuzxUtil.getString(dataMap,"materialNo");
                String inStockNo = VtuzxUtil.getString(dataMap,"stockNo");
                String inCreateTime = VtuzxUtil.getString(dataMap,"createTime");
                String inBatchNo = VtuzxUtil.getString(dataMap,"batchNo");
                double count = VtuzxUtil.getDouble(dataMap,"count");
                int res = createTime.compareTo(inCreateTime);
                if (materialNo.equals(inMateriaLNo) && stockNo.equals(inStockNo) && batchNo.equals(inBatchNo) && res >0){
                    opening += count;
                }
            }
            data.append("opening",String.format("%.2f",opening));
        }

        //入的数量减去出的数量
        for (VtuzxMap data:resultList){
            String materiakNo = VtuzxUtil.getString(data,"materialNo");
            String stockNo = VtuzxUtil.getString(data,"stockNo");
            String createTime = VtuzxUtil.getString(data,"createTime");
            String batchNo = VtuzxUtil.getString(data,"batchNo");
            double opening = VtuzxUtil.getDouble(data,"opening");
            String billNo = VtuzxUtil.getString(data,"billNo");

            for (VtuzxMap dataMap: outList){
                String inMateriaLNo = VtuzxUtil.getString(dataMap,"materialNo");
                String inStockNo = VtuzxUtil.getString(dataMap,"stockNo");
                String inCreateTime = VtuzxUtil.getString(dataMap,"createTime");
                String inBatchNo = VtuzxUtil.getString(dataMap,"batchNo");
                double count = VtuzxUtil.getDouble(dataMap,"count");
                int res = createTime.compareTo(inCreateTime);
                //计算期初用已有的期初数量减去在单据日期之前的发出数量
                if (materiakNo.equals(inMateriaLNo) && stockNo.equals(inStockNo) && batchNo.equals(inBatchNo) && res >0 ){
                    opening -= count;
                }
            }

            data.replace("opening",String.format("%.2f",opening));
        }


        //计算结存的数量
        for (VtuzxMap res:resultList){
            String mark = VtuzxUtil.getString(res,"mark");//判断是否有标记
            if(!VtuzxUtil.isEmpty(mark)){//有的话结存就是期初
                res.append("balance",VtuzxUtil.getDouble(res,"opening"));
            } else {
                double opening = VtuzxUtil.getDouble(res,"opening");
                double incomeNum = VtuzxUtil.getDouble(res,"incomeNum");
                double issueNum = VtuzxUtil.getDouble(res,"issueNum");
                double balance = opening + incomeNum - issueNum;
                String matBalance = String.format("%.2f",balance);
                res.append("balance",matBalance);
            }
        }

        //排序
        VtuzxMap createMap = null;
        for (int i = 0;i < resultList.size() - 1; i++){
            for (int j = 0; j<resultList.size() - 1 - i; j++){
                String createTime = VtuzxUtil.getString(resultList.get(j),"createTime");
                String jTime = VtuzxUtil.getString(resultList.get(j + 1),"createTime");
                if (createTime.compareTo(jTime) > 0){
                    createMap = resultList.get(j);
                    resultList.set(j, resultList.get(j + 1));
                    resultList.set(j + 1, createMap);
                }
            }
        }
        Collections.reverse(resultList);
        return new VtuzxMap(IVtuzxConst.Key_data,resultList).append(IVtuzxConst.Key_total,total);
    }

    /**
     * 下载物料收发明细
     * @param token
     * @param param
     * @return
     * @throws Exception
     */
    @Override
    public VtuzxDownloadBean downloadMaterialSummaryDetail(VtuzxToken token, Map<String, Object> param) throws Exception {
        String fMaterialNo = VtuzxUtil.getString(param,"materialNo");
        String fStockNo = VtuzxUtil.getString(param,"stockNo");
        String fBillNo = VtuzxUtil.getString(param,"billNo");
        String fBatchNo = VtuzxUtil.getString(param,"batchNo");
        String batchMaterialNo = VtuzxUtil.getString(param,"batchMaterialNo");
        List<String>  fcreateTime = VtuzxUtil.getObject(param, "createTime");
        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        Criteria cond = new Criteria();
        cond.and("status").ne("save");
        Criteria testCond = new Criteria();
        testCond.and("status").ne("save");
        //为了计算批次调整单的数据，在加两个条件
        Criteria batchCond = new Criteria();
        batchCond.and("status").ne("save");
        Criteria batchCondOr = new Criteria();
        batchCondOr.and("status").ne("save");

        Criteria allCond = new Criteria();
        if (!VtuzxUtil.isEmpty(fMaterialNo)) {
            cond.and("materialNo").is(fMaterialNo);
            testCond.and("materialNo").is(fMaterialNo);
            batchCond.and("materialNo").is(fMaterialNo);
            batchCondOr.and("materialNo").is(fMaterialNo);
            allCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(batchMaterialNo)) {
            cond.and("materialNo").is(fMaterialNo);
            testCond.and("materialNo").is(fMaterialNo);
            batchCond.and("materialNo").is(fMaterialNo);
            batchCondOr.and("materialNo").is(fMaterialNo);
            allCond.and("materialNo").is(fMaterialNo);
        }
        if (!VtuzxUtil.isEmpty(fBillNo)) {
            cond.and("billNo").regex(fBillNo);

        }
        if (!VtuzxUtil.isEmpty(fBatchNo)) {
            cond.and("batchNo").regex(fBatchNo);
            testCond.and("batchNo").regex(fBatchNo);
            batchCond.and("updateBatchNo").regex(fBatchNo);
            batchCondOr.and("updateBatchNo").regex(fBatchNo);
            allCond.and("batchNo").regex(fBatchNo);
        }
        if (!VtuzxUtil.isEmpty(fStockNo)) {
            cond.and("stockNo").is(fStockNo);
            testCond.and("toStockNo").is(fStockNo);
            batchCond.and("stockNo").is(fStockNo);
            batchCondOr.and("toStockNo").is(fStockNo);
            allCond.and("toStockNo").is(fStockNo);
        }
        if (!VtuzxUtil.isEmpty(fcreateTime)) {
            cond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            testCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            batchCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            batchCondOr.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
            allCond.and("createTime").gte(VtuzxUtil.parseDate(fcreateTime.get(0))).lte(VtuzxUtil.parseDate(fcreateTime.get(1) + " 23:59:59"));
        }
        //查询总数
        long total = otherInDetailDao.countFindMaterialSummary(new Criteria().orOperator(testCond,cond,batchCond,batchCondOr));//计算总数
        List<String> materialNoList = new ArrayList<>();
        List<String> stockNoList = new ArrayList<>();
        List<String> batchNoList = new ArrayList<>();
        //或者条件查询,调拨单据特殊,还需判断调入仓库(toStockNo)
        Iterator<Document> itr = otherInDetailDao.downloadFindMaterialSummary(new Criteria().orOperator(cond,testCond,batchCond,batchCondOr),new Document("createTime",-1),pageIndex,pageSize);
        List<VtuzxMap> resultList = new ArrayList<>();
        while(itr.hasNext()) {
            Document v = itr.next();
            Object value = v.get("allValue");
            String billNo = VtuzxUtil.getString(v,"billNo");//单据号
            //存在没有单据编号但是有单据信息的情况,加一个判断
            if (!VtuzxUtil.isEmpty(billNo)){
                String materialNo = VtuzxUtil.getString(v,"materialNo");
                String batchNo = VtuzxUtil.getString(v,"batchNo");//批次
                String updateBatchNo = VtuzxUtil.getString(v,"updateBatchNo");//批次调整单修改后的批次
                String stockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
                String toStockNo = VtuzxUtil.getString(v,"toStockNo");//入库仓库编号
                double count = VtuzxUtil.getDouble(v,"count");
                if (!materialNoList.contains(materialNo)){
                    materialNoList.add(materialNo);
                }
                //如果批次的查询条件不为空的话
                if (!VtuzxUtil.isEmpty(fBatchNo)){
                    if(!VtuzxUtil.isEmpty(updateBatchNo)){//修改后批次也不为空说明是批次调整单
                        //需要判断是出库仓库还是入库仓库
                        if(batchNo.equals(fBatchNo)){
                            if(!batchNoList.contains(batchNo)){
                                batchNoList.add(batchNo);
                            }
                        } else if (updateBatchNo.equals(fBatchNo)){
                            if(!batchNoList.contains(batchNo)){
                                batchNoList.add(batchNo);
                            }
                        }
                    } else {//正常单据
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                    }
                } else {//如果批次查询条件为空的话
                    //判断是不是调拨单
                    if(!VtuzxUtil.isEmpty(batchNo) && !VtuzxUtil.isEmpty(updateBatchNo)){
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                        if(!batchNoList.contains(updateBatchNo)){
                            batchNoList.add(updateBatchNo);
                        }
                    } else {
                        if(!batchNoList.contains(batchNo)){
                            batchNoList.add(batchNo);
                        }
                    }
                }



                //如果仓库的查询条件不为空的话
                if (!VtuzxUtil.isEmpty(fStockNo)){
                    if(!VtuzxUtil.isEmpty(toStockNo)){//调入仓库也不为空说明是调拨单
                        //需要判断是出库仓库还是入库仓库
                        if(stockNo.equals(fStockNo)){
                            if(!stockNoList.contains(stockNo)){
                                stockNoList.add(stockNo);
                            }
                        } else if (toStockNo.equals(fStockNo)){
                            if(!stockNoList.contains(toStockNo)){
                                stockNoList.add(toStockNo);
                            }
                        }
                    } else {//正常单据
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    }
                } else {//如果仓库查询条件为空的话
                    //判断是不是调拨单
                    if(!VtuzxUtil.isEmpty(toStockNo) && !VtuzxUtil.isEmpty(stockNo)){
                        if(!stockNoList.contains(toStockNo)){
                            stockNoList.add(toStockNo);
                        }
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    } else {
                        if(!stockNoList.contains(stockNo)){
                            stockNoList.add(stockNo);
                        }
                    }
                }

//                if (!batchNoList.contains(batchNo)){
//                    batchNoList.add(batchNo);
//                }

                VtuzxMap dataMap = new VtuzxMap();
                dataMap.append("materialNo",materialNo)
                        .append("batchNo",batchNo)
                        .append("billNo",billNo)
                        .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                        .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                        .append("stockNo",stockNo);
                //issueNum : 发出数
                //incomeNum 入库数量
                if (billNo.startsWith("SCLL")){
                    dataMap.append("billName","生产领料单")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("QTCK")){
                    Document otherOutDoc = otherOutDao.findOne(Criteria.where("billNo").is(billNo));
                    dataMap.append("billName","其他出库单")
                            .append("auditTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(otherOutDoc,"auditTime")))
                            .append("issueNum",count);
                }
                if (billNo.startsWith("XSCK")){
                    dataMap.append("billName","销售出库")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("DBYK")){
                    //如果调出仓库和调入仓库都一样的话
                    if (stockNo.equals(fStockNo) && toStockNo.equals(fStockNo)){
                        dataMap.append("billName","直接调拨单")
                                .append("mark","mark")
                                .append("issueNum",count);
                        VtuzxMap inAllocation = new VtuzxMap();
                        inAllocation.append("materialNo",materialNo)
                                .append("batchNo",batchNo)
                                .append("billNo",billNo)
                                .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                .append("stockNo",toStockNo)
                                .append("mark","mark")
                                .append("billName","直接调拨单(入)")
                                .append("incomeNum",count);
                        resultList.add(inAllocation);
                        total += 1;
                    } else {//不一样的情况判断有没有仓库查询条件
                        if(!VtuzxUtil.isEmpty(fStockNo)){
                            if(stockNo.equals(fStockNo)){//调出仓库与查询的仓库相同
                                dataMap.append("billName","直接调拨单")
                                        .append("issueNum",count);
                                if (stockNo.equals(toStockNo)){
                                    dataMap.append("mark","mark");
                                }
                            } else if(toStockNo.equals(fStockNo)){
                                dataMap.append("billName","直接调拨单(入)")
                                        .append("stockNo",toStockNo)
                                        .append("incomeNum",count);
                                if (stockNo.equals(toStockNo)){
                                    dataMap.append("mark","mark");
                                }
                            }
                        } else {//如果仓库查询条件为空
                            dataMap.append("billName","直接调拨单")
                                    .append("issueNum",count);
                            VtuzxMap inAllocation = new VtuzxMap();
                            inAllocation.append("materialNo",materialNo)
                                    .append("batchNo",batchNo)
                                    .append("billNo",billNo)
                                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                    .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                    .append("stockNo",toStockNo)
                                    .append("billName","直接调拨单(入)")
                                    .append("incomeNum",count);
                            resultList.add(inAllocation);
                            total += 1;
                        }

                    }
                }
                if (billNo.startsWith("CGRK")){
                    dataMap.append("billName","采购入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("CJRK")){
                    dataMap.append("billName","生产入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("QTRK")){
                    dataMap.append("billName","其它入库单")
                            .append("incomeNum",count);
                }
                if (billNo.startsWith("SCBL")){
                    dataMap.append("billName","生产补料")
                            .append("issueNum",count);
                }
                if (billNo.startsWith("TZDD")){
                    if(!VtuzxUtil.isEmpty(fBatchNo)){
                        if(batchNo.equals(fBatchNo)){//前批次与查询的批次相同
                            dataMap.append("billName","批次调整单")
                                    .append("issueNum",count);
                        } else if(updateBatchNo.equals(fBatchNo)){
                            dataMap.append("billName","批次调整单(入)")
                                    .append("batchNo",updateBatchNo)
                                    .append("incomeNum",count);

                        }
                    } else {//如果批次查询条件为空
                        dataMap.append("billName","批次调整单")
                                .append("issueNum",count);
                        VtuzxMap inAllocation = new VtuzxMap();
                        inAllocation.append("materialNo",materialNo)
                                .append("batchNo",updateBatchNo)
                                .append("billNo",billNo)
                                .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                                .append("updateTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"updateTime")))
                                .append("stockNo",stockNo)
                                .append("billName","批次调整单(入)")
                                .append("incomeNum",count);
                        resultList.add(inAllocation);
                        total += 1;
                    }


                }
                resultList.add(dataMap);
            }

        }
        //去物料表查询物料名
        List<VtuzxMap> materList = new ArrayList<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            String materialNo = VtuzxUtil.getString(materialDoc,"fNumber");
            String materialName = VtuzxUtil.getString(materialDoc,"fName");
            String unit = VtuzxUtil.getString(materialDoc,"fBaseUnitIdFName");
            String fSpecification = VtuzxUtil.getString(materialDoc,"fSpecification");
            VtuzxMap materialMap = new VtuzxMap();
            materialMap.append("materialNo",materialNo)
                    .append("materialName",materialName)
                    .append("fSpecification",fSpecification)
                    .append("unit",unit);
            materList.add(materialMap);
        }
        //把物料名称添加上
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "materialNo");
            for (VtuzxMap matData : materList) {
                String materialNo = VtuzxUtil.getString(matData, "materialNo");
                String materialName = VtuzxUtil.getString(matData, "materialName");
                String unit = VtuzxUtil.getString(matData,"unit");
                String fSpecification = VtuzxUtil.getString(matData,"fSpecification");
                if (fNumber.equals(materialNo)) {
                    data.append("materialName",materialName)
                            .append("fSpecification",fSpecification)
                            .append("unit",unit);
                    continue;
                }
            }
        }

        Map<String,Object> stockMap = new HashMap<>();
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockNoList),null,0,0);
        while (stockItr.hasNext()){
            Document stockDoc = stockItr.next();
            String stockNo = VtuzxUtil.getString(stockDoc,"fNumber");
            String stockName = VtuzxUtil.getString(stockDoc,"fName");
            stockMap.put(stockNo,stockName);
        }
        //把仓库名放到resultlist
        for (VtuzxMap data:resultList) {
            String fNumber = VtuzxUtil.getString(data, "stockNo");
            String stockName = VtuzxUtil.getString(stockMap,fNumber);
            data.append("stockName",stockName);
        }


        //计算期入
        List<VtuzxMap> inList = new ArrayList<>();//存放入库
        List<VtuzxMap> outList = new ArrayList<>();//存放出库
        Criteria inCond = new Criteria("materialNo").in(materialNoList).and("batchNo").in(batchNoList).and("toStockNo").in(stockNoList);
        Criteria inBatchCond = new Criteria("materialNo").in(materialNoList).and("updateBatchNo").in(batchNoList).and("toStockNo").in(stockNoList);
        Criteria inCondOr = new Criteria("materialNo").in(materialNoList).and("batchNo").in(batchNoList).and("stockNo").in(stockNoList);
        Criteria inBatchCondOr = new Criteria("materialNo").in(materialNoList).and("updateBatchNo").in(batchNoList).and("stockNo").in(stockNoList);
        //查询对应物料所有入和出数据(不区分时间)
        Iterator<Document> otherItr = otherInDetailDao.findInDetail(new Criteria().orOperator(inCond,inBatchCond,inCondOr,inBatchCondOr));
        while(otherItr.hasNext()) {
            Document v = otherItr.next();
            Object value = v.get("allValue");
            String materialNo = VtuzxUtil.getString(v,"materialNo");
            String stockNo = "";
            //String batchNo = VtuzxUtil.getString(v, "batchNo");
            String batchNo = "";
            String billNo = VtuzxUtil.getString(v, "billNo");
            if(billNo.startsWith("DBYK")){
                //如果是调拨单的话需要取入库仓库编号
                stockNo = VtuzxUtil.getString(v,"toStockNo");//仓库编号
            } else {
                stockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
            }
            if(billNo.startsWith("TZDD")){
                //如果是调拨单的话需要取入库仓库编号
                batchNo = VtuzxUtil.getString(v,"updateBatchNo");//仓库编号
            } else {
                batchNo = VtuzxUtil.getString(v,"batchNo");//仓库编号
            }
            double count = VtuzxUtil.getDouble(v,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("materialNo",materialNo)
                    .append("stockNo",stockNo)
                    .append("batchNo",batchNo)
                    .append("billNo",billNo)
                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(v,"createTime")))
                    .append("count",count);
            inList.add(dataMap);
        }
        //查询所有出库数据
        Criteria outCond = new Criteria("allValue.materialNo").in(materialNoList).and("allValue.batchNo").in(batchNoList).and("allValue.stockNo").in(stockNoList);
        outCond.and("allValue.status").ne("save");

        Criteria outCondOr = new Criteria("allValue.materialNo").in(materialNoList).and("allValue.updateBatchNo").in(batchNoList).and("allValue.stockNo").in(stockNoList);
        outCondOr.and("allValue.status").ne("save");
        Iterator<Document> outItr = materialBomPickDetailDao.findOutDetail(new Criteria().orOperator(outCond,outCondOr));
        while(outItr.hasNext()) {
            Document v = outItr.next();
            Object value = v.get("allValue");
            String materialNo = VtuzxUtil.getString((Map<String, Object>) value,"materialNo");
            String batchNo = VtuzxUtil.getString((Map<String, Object>) value,"batchNo");
            String stockNo = VtuzxUtil.getString((Map<String, Object>) value,"stockNo");//仓库编号
            //String createTime = VtuzxUtil.getString((Map<String, Object>) value,"createTime");
            double count = VtuzxUtil.getDouble((Map<String, Object>) value,"count");
            VtuzxMap dataMap = new VtuzxMap();
            dataMap.append("materialNo",materialNo)
                    .append("stockNo",stockNo)
                    .append("batchNo",batchNo)
                    .append("createTime",VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject((Map<String, Object>) value,"createTime")))
                    .append("count",count);
            outList.add(dataMap);
        }

        //计算入的数量
        for (VtuzxMap data:resultList){
            String materialNo = VtuzxUtil.getString(data,"materialNo");
            String stockNo = VtuzxUtil.getString(data,"stockNo");
            String createTime = VtuzxUtil.getString(data,"createTime");
            String batchNo = VtuzxUtil.getString(data,"batchNo");
            double opening = 0d;//期初数
            for (VtuzxMap dataMap: inList){
                String inMateriaLNo = VtuzxUtil.getString(dataMap,"materialNo");
                String inStockNo = VtuzxUtil.getString(dataMap,"stockNo");
                String inCreateTime = VtuzxUtil.getString(dataMap,"createTime");
                String inBatchNo = VtuzxUtil.getString(dataMap,"batchNo");
                double count = VtuzxUtil.getDouble(dataMap,"count");
                int res = createTime.compareTo(inCreateTime);
                if (materialNo.equals(inMateriaLNo) && stockNo.equals(inStockNo) && batchNo.equals(inBatchNo) && res >0){
                    opening += count;
                }
            }
            data.append("opening",String.format("%.2f",opening));
        }

        //入的数量减去出的数量
        for (VtuzxMap data:resultList){
            String materiakNo = VtuzxUtil.getString(data,"materialNo");
            String stockNo = VtuzxUtil.getString(data,"stockNo");
            String createTime = VtuzxUtil.getString(data,"createTime");
            String batchNo = VtuzxUtil.getString(data,"batchNo");
            double opening = VtuzxUtil.getDouble(data,"opening");
            String billNo = VtuzxUtil.getString(data,"billNo");

            for (VtuzxMap dataMap: outList){
                String inMateriaLNo = VtuzxUtil.getString(dataMap,"materialNo");
                String inStockNo = VtuzxUtil.getString(dataMap,"stockNo");
                String inCreateTime = VtuzxUtil.getString(dataMap,"createTime");
                String inBatchNo = VtuzxUtil.getString(dataMap,"batchNo");
                double count = VtuzxUtil.getDouble(dataMap,"count");
                int res = createTime.compareTo(inCreateTime);
                //计算期初用已有的期初数量减去在单据日期之前的发出数量
                if (materiakNo.equals(inMateriaLNo) && stockNo.equals(inStockNo) && batchNo.equals(inBatchNo) && res >0 ){
                    opening -= count;
                }
            }

            data.replace("opening",String.format("%.2f",opening));
        }


        //计算结存的数量
        for (VtuzxMap res:resultList){
            String mark = VtuzxUtil.getString(res,"mark");//判断是否有标记
            if(!VtuzxUtil.isEmpty(mark)){//有的话结存就是期初
                res.append("balance",VtuzxUtil.getDouble(res,"opening"));
            } else {
                double opening = VtuzxUtil.getDouble(res,"opening");
                double incomeNum = VtuzxUtil.getDouble(res,"incomeNum");
                double issueNum = VtuzxUtil.getDouble(res,"issueNum");
                double balance = opening + incomeNum - issueNum;
                String matBalance = String.format("%.2f",balance);
                res.append("balance",matBalance);
            }
        }

        //排序
        VtuzxMap createMap = null;
        for (int i = 0;i < resultList.size() - 1; i++){
            for (int j = 0; j<resultList.size() - 1 - i; j++){
                String createTime = VtuzxUtil.getString(resultList.get(j),"createTime");
                String jTime = VtuzxUtil.getString(resultList.get(j + 1),"createTime");
                if (createTime.compareTo(jTime) > 0){
                    createMap = resultList.get(j);
                    resultList.set(j, resultList.get(j + 1));
                    resultList.set(j + 1, createMap);
                }
            }
        }
        Collections.reverse(resultList);

        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "物料收发明细");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "物料编号"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "批次"),
                new VtuzxExcelCell(col++, "单据日期"),
                new VtuzxExcelCell(col++, "单据名称"),
                new VtuzxExcelCell(col++, "单据编号"),
                new VtuzxExcelCell(col++, "仓库"),
                new VtuzxExcelCell(col++, "单位"),
                new VtuzxExcelCell(col++, "期初"),
                new VtuzxExcelCell(col++, "收入"),
                new VtuzxExcelCell(col++, "发出"),
                new VtuzxExcelCell(col++, "结存")

        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : resultList) {

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "fSpecification")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "createTime")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "unit")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "opening")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "incomeNum")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "issueNum")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "balance"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 200},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 200},
                new int[]{7, 100},
                new int[]{8, 100},
                new int[]{9, 100},
                new int[]{10, 150},
                new int[]{11, 150},
                new int[]{12, 100}

        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("物料收发明细" + timestamp + ".xlsx", stream);

    }



    @Override
    public double getOneOpening(String materialNo, String stockNo, String createTime,String batchNo) {
        Criteria cond = new Criteria();
        cond.and("materialNo").is(materialNo).and("stockNo").is(stockNo)
                .and("batchNo").is(batchNo);
        cond.and("createTime").lte(VtuzxUtil.parseDate(createTime));

        //查询对应物料在创建时间之前所有入的数量
        List<VtuzxMap> inList = new ArrayList<>();
        Iterator<Document> inDetailItr = otherInDetailDao.findInDetail(cond);
        while(inDetailItr.hasNext()) {
            Document v = inDetailItr.next();
            Object value = v.get("allValue");
            String fMaterialNo = VtuzxUtil.getString(v,"materialNo");
            String fStockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
            double count = VtuzxUtil.getDouble(v,"count");
            VtuzxMap dataMap = new VtuzxMap();
                dataMap.append("materialNo",fMaterialNo)
                        .append("stockNo",fStockNo)
                        .append("count",count);
                inList.add(dataMap);
        }
        //查询对应物料在创建时间之前所有出的数量
        List<VtuzxMap> outList = new ArrayList<>();
        Iterator<Document> outDetailItr = materialBomPickDetailDao.findOutDetail(cond);
        while(outDetailItr.hasNext()) {
            Document v = outDetailItr.next();
            Object value = v.get("allValue");
            String fMaterialNo = VtuzxUtil.getString(v,"materialNo");
            String fStockNo = VtuzxUtil.getString(v,"stockNo");//仓库编号
            double count = VtuzxUtil.getDouble(v,"count");
            VtuzxMap dataMap = new VtuzxMap();
                dataMap.append("materialNo",fMaterialNo)
                        .append("stockNo",fStockNo)
                        .append("count",count);
                outList.add(dataMap);
        }

        double inCount = 0;
        double outCount = 0;
        for (VtuzxMap data:inList){
            double count = VtuzxUtil.getDouble(data,"count");
            inCount = inCount + count;
        }
        for (VtuzxMap data:outList){
            double count = VtuzxUtil.getDouble(data,"count");
            outCount = outCount + count;
        }
        double opening = inCount - outCount;
        return opening;

    }

    @Override
    public VtuzxMap getAllOutStockBill(ObjectId userId, String name, String billNo, String materialNo, String billType, List<String> createTime, int pageIndex, int pageSize) throws VtuzxException {
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(createTime)) {
            cond.and("createTime").gte(VtuzxUtil.parseDate(createTime.get(0))).lte(VtuzxUtil.parseDate(createTime.get(1) + " 23:59:59"));
        }
        if (!VtuzxUtil.isEmpty(billNo)) {
            cond.and("billNo").regex(billNo);
        }
        if (!VtuzxUtil.isEmpty(materialNo)) {
            cond.and("materialNo").is(materialNo);
        }
        VtuzxPage page = null;
        if ("SCLL".equals(billType)) {
            page = prdPickDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        } else if ("LLDLL".equals(billType)) {
            page = materialBomPickDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        } else if ("SCBL".equals(billType)) {
            page = materialBomSupplementDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        } else if ("XSCK".equals(billType)) {
            page = saleOutDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        } else if ("DBYK".equals(billType)) {
            page = allocationDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        } else if ("QTCK".equals(billType)) {
            page = otherOutDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        }
        assert page != null;
        List<VtuzxMap> dataList = page.iterator(doc -> {
            VtuzxMap data = new VtuzxMap(doc);
            data.append("createTime", (VtuzxUtil.formatYmdHmF(VtuzxUtil.getObject(data, "createTime"))));
            return data;
        });
        return new VtuzxMap(IVtuzxConst.Key_data, dataList).append(IVtuzxConst.Key_total, page.total());
    }

    @Override
    public void handOutStock(ObjectId userId, String name, Map<String, Object> param) throws VtuzxException {
        String billType = VtuzxUtil.getString(param, "billType");
        List<String> billNoList = VtuzxUtil.getObject(param, "billNoList");
        for (String billNo: billNoList) {
            Criteria cond = new Criteria("billNo").is(billNo);
            Iterator<Document> boxItr = handOutStockItr(billType, cond);
            if ("LLDLL".equals(billType)) {
                billType = "SCLL";
            }
            List<String> boxNoList = new ArrayList<>();
            String stockNo = "";
            while (boxItr.hasNext()) {
                Document boxDoc = boxItr.next();
                String boxNo = VtuzxUtil.getString(boxDoc, "boxNo");
                boxNoList.add(boxNo);
            }

            List<String> boxNoOutList = new ArrayList<>();
            List<VtuzxMap> bcBoxList = new ArrayList<>();
            Iterator<Document> invDetailItr = inventoryDetailDao.findList(Criteria.where("boxNo").in(boxNoList).and("status").is("in"), null, 0, 0);
            while (invDetailItr.hasNext()) {
                Document invDetailDoc = invDetailItr.next();
                boxNoOutList.add(VtuzxUtil.getString(invDetailDoc, "boxNo"));
            }

            if (!VtuzxUtil.isEmpty(boxNoOutList)) {
                inventoryService.reduceInventory(userId, name, BillEvent.Audit, BillType.getEnum(billType), stockNo, billNo, boxNoList, bcBoxList);
            }

        }
    }

    private Iterator<Document> handOutStockItr(String billType, Criteria cond) {
        Iterator<Document> boxItr = null;
        if ("SCLL".equals(billType)) {
            boxItr = prdPickDetailBoxDao.findList(cond, new Document("createTime", -1), 0,0);
        } else if ("LLDLL".equals(billType)) {
            boxItr = materialBomPickDetailBoxDao.findList(cond, new Document("createTime", -1), 0, 0);
        } else if ("SCBL".equals(billType)) {
            boxItr = materialBomSupplementDetailBoxDao.findList(cond, new Document("createTime", -1), 0, 0);
        } else if ("XSCK".equals(billType)) {
            boxItr = saleOutDetailBoxDao.findList(cond, new Document("createTime", -1), 0, 0);
        } else if ("DBYK".equals(billType)) {
            boxItr = allocationDetailBoxDao.findList(cond, new Document("createTime", -1), 0, 0);
        } else if ("QTCK".equals(billType)) {
            boxItr = otherOutDetailBoxDao.findList(cond, new Document("createTime", -1), 0, 0);
        }
        return boxItr;
    }
    @Override
    public void syncInventory(String name,ObjectId userId)throws Exception {

        createSerial();
//        createPrdIn();
        createPrdInDetail();
        createBox();

    }

    //调整成品入库重复
    private void changePrdin(){

//        String serialNo = "http://zs.xiaoqin.com.cn/xqc_h5?traceabilityCode=Z1679027689004";
//        Document inventoryDoc = inventoryDetailDao.findOne(Criteria.where("traceBack.traceBackNo").is(serialNo));
//
        System.out.println("");
        String billNo = "CJSH231209083355362";
        UpdateResult updateResult = prdColDetailDao.updateMany(Criteria.where("billNo").is(billNo), new Document("inStock", true));
        System.out.println("");
//        int a = 0;
//        Iterator<Document> prdInItr = prdInDetailBoxDao.findList(Criteria.where("billNo").is(billNo),null,0,0);
//        while (prdInItr.hasNext()){
//            a++;
//            Document prdInDoc = prdInItr.next();
//            String boxNo = VtuzxUtil.getString(prdInDoc,"boxNo");
//            DeleteResult deleteResult = inventoryDetailDao.deleteOne(Criteria.where("boxNo").is(boxNo).and("transactionKey").is("1702341747681MuTCdc"));
//            System.out.println(a);
//        }




    }
    //补车间收货
    private void prdCol(){
        Criteria cond = new Criteria();
        cond.and("updateTime").gte(VtuzxUtil.parseDate("2023-12-06 07:30:00")).lte(VtuzxUtil.parseDate("2023-12-06 09:00:00"));
        cond.and("lastTime").exists(false);
        List<Document> traceList = new ArrayList<>();
        List<String> boxNoList = new ArrayList<>();
        Iterator<Document> teaceItr = traceabilityCodeDetailDao.findList(cond,null,0,0);
        while (teaceItr.hasNext()){
            Document traceDoc = teaceItr.next();
            String boxNo = VtuzxUtil.getString(traceDoc,"boxNo");
            boxNoList.add(boxNo);
            traceList.add(traceDoc);
        }
        List<Document> addList = new ArrayList<>();//最后需要添加的数据
        List<String> inventoryBoxList = new ArrayList<>();//zaikudexianghao
        Iterator<Document> inventoryItr = prdColDetailDao.findList(Criteria.where("boxNo").in(boxNoList),null,0,0);
        List<Document> inventoryList = new ArrayList<>();
        while (inventoryItr.hasNext()){
            Document inventoryDoc = inventoryItr.next();
            String inventoryBoxNo = VtuzxUtil.getString(inventoryDoc,"boxNo");

            inventoryBoxList.add(inventoryBoxNo);

            inventoryList.add(inventoryDoc);
        }


        //循环trace集合 判断箱号不在这个循环里的就是需要添加的数据
        for (Document data : traceList){
            String boxNo = VtuzxUtil.getString(data,"boxNo");
            if (!inventoryBoxList.contains(boxNo)){
                addList.add(data);
            }
        }

        System.out.println("");

        Map<String,Object> dataMap = new HashMap<>();//k xiangaho  v ;traceback
        for (Document data : addList){
            String boxNo = VtuzxUtil.getString(data,"boxNo");
            String traceNo = VtuzxUtil.getString(data,"traceabilityCode");
            List<String> traceBack = (List<String>) dataMap.get(boxNo);
            if (traceBack == null){
                List<String> list = new ArrayList<>();
                list.add(traceNo);
                dataMap.put(boxNo,list);

            } else {
                traceBack.add(traceNo);
                dataMap.put(boxNo,traceBack);
            }
        }
        System.out.println("");
        List<Document> prdColList = new ArrayList<>();
        for (Map.Entry entry:dataMap.entrySet()){
            String boxNo = (String) entry.getKey();
            List<String> traceBack = (List<String>) entry.getValue();
            Document prdColDoc = new Document();
            prdColDoc.append("materialNo","04030303002")
                    .append("materialModel","7-9头/500g")
                    .append("materialName","晓芹冷冻海参7-9头/500g-网购专供")
                    .append("unitName","斤")
                    .append("batchNo","B371026")
                    .append("count",3)
                    .append("inStock",false)
                    .append("insertUserName","sunyf")
                    .append("standard",3)
                    .append("isDelete",false)
                    .append("currentCount",3)
                    .append("traceCount",1)
                    .append("traceBack",traceBack)
                    .append("boxNo",boxNo)
                    .append("billNo","CJSH231206082625907")
                    .append("produceDate","2023-11-28")
                    .append("taskBillNo","SCRW231205091540909");
            prdColList.add(prdColDoc);

        }

        System.out.println("");
        prdColDetailDao.insertMany(prdColList);
    }


    @Override
    public VtuzxDownloadBean downloadInventorySerial(VtuzxToken token, Map<String, Object> param)throws Exception {
        VtuzxDownloadBean vtuzxDownloadBean = prdInSerial();
        return vtuzxDownloadBean;
//        String startTime = "2023-07-14 00:00:00";
//        String endTime = "2023-07-14 23:59:59";
//        Criteria cond = new Criteria();
//        cond.and("createTime").gte(VtuzxUtil.parseDate(startTime)).lte(VtuzxUtil.parseDate(endTime));
//        Iterator<Document> inventoryItr = inventoryDetailDao.findList(cond,new Document("createTime",-1),0,0);
//        List<VtuzxMap> inventoryList = new ArrayList<>();//所有追溯信息
//        while (inventoryItr.hasNext()){
//            Document inventoryDoc = inventoryItr.next();
//            String status = VtuzxUtil.getString(inventoryDoc,"status");
//            if ("in".equals(status)){//只查询在库的
//                List<Document> traceBack =VtuzxUtil.getObject(inventoryDoc,"traceBack");
//                if (traceBack != null && traceBack.size() > 0) {
//                    VtuzxMap inventoryMap = new VtuzxMap(inventoryDoc);
//                    inventoryList.add(inventoryMap);
//                }
//            }
//
//        }
//        List<VtuzxMap> dataList = new ArrayList<>();//需要导出的数据
//        for (VtuzxMap data:inventoryList){
//            String boxNo = VtuzxUtil.getString(data,"boxNo");
//            String stockNo = VtuzxUtil.getString(data,"stockNo");
//            List<Document> traceBack = VtuzxUtil.getObject(data,"traceBack");
//            for (Document trace:traceBack){
//                String status = VtuzxUtil.getString(trace,"status");
//                if ("in".equals(status)){//追溯码在库
//                    String traceBackNo = VtuzxUtil.getString(trace,"traceBackNo");
//                    VtuzxMap map = new VtuzxMap();
//                    map.append("boxNo",boxNo)
//                            .append("traceBackNo",traceBackNo)
//                            .append("stockNo",stockNo);
//                    dataList.add(map);
//                }
//            }
//        }
//
//
//        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
//
//        List<VtuzxExcelSheet> sheets = new ArrayList<>();
//        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "唯一码");
//        int col = 0;
//        int row = 0;
//        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
//                new VtuzxExcelCell(col++,"追溯码"),
//                new VtuzxExcelCell(col++,"箱码"),
//                new VtuzxExcelCell(col++,"仓库")
//        ));
//        sheet0.addRow(header0);
//
//        for (VtuzxMap data : dataList) {
//
//            col = 0;
//            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
//
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data,"traceBackNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data,"boxNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data,"stockNo"))
//            ));
//
//            sheet0.addRow(dataRow);
//        }
//
//        sheet0.setColumnWidth(new VtuzxArrayList<>(
//                new int[]{0, 500},
//                new int[]{0, 200}
//
//        ));
//
//        sheets.add(sheet0);
//        ByteArrayOutputStream stream = new ByteArrayOutputStream();
//        VtuzxExcel.write(stream, sheets);
//        return new VtuzxDownloadBean("唯一码详情" + timestamp + ".xlsx", stream);
    }

    /**
     * 唯一码用销售出
     * @throws Exception
     */
    @Override
    public void serialSaleOut(ObjectId _userId, String name) throws Exception {
        serialOut(_userId,name,0,30);
    }

    //下载领料入库
    private VtuzxDownloadBean prdInSerial() throws Exception{
//        String startTime = "2023-07-07 00:00:00";
//        String endTime = "2023-07-13 23:59:59";
        Criteria cond = new Criteria();
        cond.and("billNo").is("CJRK230611141857045");
//        cond.and("createTime").gte(VtuzxUtil.parseDate(startTime)).lte(VtuzxUtil.parseDate(endTime));
        Iterator<Document> prdInDetailItr = prdInDetailBoxDao.findList(cond,new Document("createTime",-1),0,0);
        List<VtuzxMap> prdInDetailList = new ArrayList<>();//所有追溯信息
        while (prdInDetailItr.hasNext()){
            Document prdInDetailDoc = prdInDetailItr.next();
            List<Document> traceBack =VtuzxUtil.getObject(prdInDetailDoc,"traceBack");
            //只查询带追溯的
            if (traceBack != null && traceBack.size() > 0) {
                VtuzxMap inventoryMap = new VtuzxMap(prdInDetailDoc);
                prdInDetailList.add(inventoryMap);
            }

        }
        List<VtuzxMap> dataList = new ArrayList<>();//需要导出的数据
        for (VtuzxMap data:prdInDetailList){
            String boxNo = VtuzxUtil.getString(data,"boxNo");
            List<Document> traceBack = VtuzxUtil.getObject(data,"traceBack");
            for (Document trace:traceBack){
                String status = VtuzxUtil.getString(trace,"status");
                if ("in".equals(status)){//追溯码在库
                    String traceBackNo = VtuzxUtil.getString(trace,"traceBackNo");
                    VtuzxMap map = new VtuzxMap();
                    map.append("boxNo",boxNo)
                            .append("traceBackNo",traceBackNo);
                    dataList.add(map);
                }
            }
        }


        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "唯一码");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++,"追溯码"),
                new VtuzxExcelCell(col++,"箱码")
        ));
        sheet0.addRow(header0);

        for (VtuzxMap data : dataList) {

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(

                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data,"traceBackNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(data,"boxNo"))
            ));

            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 500},
                new int[]{0, 200}

        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("唯一码详情" + timestamp + ".xlsx", stream);
    }
    private void serialOut(ObjectId _userId, String name, int start, int size) throws Exception{
        Criteria cond = new Criteria();
        String stockNo = "015";
        cond.and("stockNo").is(stockNo);//仓库
        Iterator<Document> inventoryItr = inventoryDetailDao.findList(cond,new Document("createTime",-1),start,size);
        List<VtuzxMap> inventoryList = new ArrayList<>();
        int number = 0;
        while (inventoryItr.hasNext()){
            number++;
            Document inventoryDoc = inventoryItr.next();
            List<Document> traceBack =VtuzxUtil.getObject(inventoryDoc,"traceBack");
            if (traceBack != null && traceBack.size() > 0) {
                VtuzxMap inventoryMap = new VtuzxMap(inventoryDoc);
                inventoryList.add(inventoryMap);
            }
        }
        List<String> boxList = new ArrayList<>();

        for (VtuzxMap data:inventoryList){
            String status = VtuzxUtil.getString(data,"status");
            if ("in".equals(status)){
                String boxNo = VtuzxUtil.getString(data,"boxNo");
                boxList.add(boxNo);
            }
        }
        for (String boxNo:boxList){
            VtuzxMap materialBox = getMaterialBox(stockNo,null,boxNo);
            List<Map<String,Object>> dataList = VtuzxUtil.getObject(materialBox,"data");
            iXqcSaleService.addUserSaleOutMaterialBoxList(_userId,name,stockNo,dataList);

        }
        if (boxList != null && boxList.size() > 0){
            Map<String,Object> param = new LinkedHashMap<>();
            param.put("stockNo",stockNo);
            param.put("pickType","外地");
            iXqcSaleService.saveUserSaleOut(_userId,name,param);
        }

        System.out.println(start+"-----------------------");
        if (number >= size){
            serialOut(_userId,name,start +1,size);
        }
    }

    /**
     * 修改追溯码
     * @param userId
     * @param name
     * @param param
     * @throws VtuzxException
     */
    @Override
    public VtuzxMap changeTraceCode(ObjectId userId, String name, Map<String, Object> param) throws VtuzxException {
        String oldTrace = VtuzxUtil.getString(param,"oldTrace");
        String newTrace = VtuzxUtil.getString(param,"newTrace");
        //校验数据
        long count = traceabilityCodeDetailDao.count(Criteria.where("traceabilityCode").is(oldTrace).and("enableStatus").is(true));
        if (count <= 0){
            throw new VtuzxException("旧追溯码不存在或还未启用");
        }
        long newCount = traceabilityCodeDetailDao.count(Criteria.where("traceabilityCode").is(newTrace).and("enableStatus").is(false));
        if (newCount <= 0){
            throw new VtuzxException("新追溯码不存在或已启用");
        }
        VtuzxMap detailByTrace = getDetailByTrace(oldTrace);
        VtuzxMap dataByTrace = VtuzxUtil.getObject(detailByTrace,"data");
        String materialNo = VtuzxUtil.getString(dataByTrace,"materialNo");
        String materialName = VtuzxUtil.getString(dataByTrace,"materialName");
        //修改追溯码详情表
        Document traceabilityDoc = traceabilityCodeDetailDao.findOne(Criteria.where("traceabilityCode").is(oldTrace).and("enableStatus").is(true));
        String traceId = VtuzxUtil.getString(traceabilityDoc,"_id");
        String boxNo = VtuzxUtil.getString(traceabilityDoc,"boxNo");
        traceabilityDoc.replace("boxNo","");
        traceabilityCodeDetailDao.updateById(traceId,traceabilityDoc);
        traceabilityCodeDetailDao.updateOne(Criteria.where("traceabilityCode").is(newTrace),new Document("boxNo",boxNo).append("enableStatus",true));

       //修改车间收货里的追溯码
        Document prdColDoc = prdColDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
        String prdId = VtuzxUtil.getString(prdColDoc,"_id");
        List<String> prdTraceBack = VtuzxUtil.getObject(prdColDoc,"traceBack");
        int i = 0;
        for (String trace : prdTraceBack){
            if (trace.equals(oldTrace)){
                break;
            }
            i++;
        }
        prdTraceBack.set(i,newTrace);
        prdColDoc.replace("traceBack",prdTraceBack);
        prdColDetailDao.updateById(prdId,prdColDoc);
        //修改即时库存里的追溯码
        Document inventoryDoc = inventoryDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
        if (!VtuzxUtil.isEmpty(inventoryDoc)){
            String _id = VtuzxUtil.getString(inventoryDoc,"_id");
            List<Document> traceBack = VtuzxUtil.getObject(inventoryDoc,"traceBack");
            for (Document trace:traceBack){
                String traceBackNo = VtuzxUtil.getString(trace,"traceBackNo");
                if (traceBackNo.equals(oldTrace)){
                    trace.replace("traceBackNo",newTrace);
                }
            }
            inventoryDoc.replace("traceBack",traceBack);
            inventoryDetailDao.updateById(_id,inventoryDoc);
        }
        //获取令牌
        Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
        VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
        //需要先获取条码(skubarcode)
        VtuzxMap goodsParam = new VtuzxMap();
        goodsParam.append("pageIndex", 0)
                .append("pageSize", 1)
                .append("goodsNo", materialNo);
        try {
            goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
        } catch (Exception e) {
            throw new VtuzxException(e.getMessage());
        }

        LinkedHashMap result = VtuzxUtil.getObject(goodsMap, "result");
        LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result, "data");
        List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data, "goods");
        LinkedHashMap goodsResult = goods.get(0);
        String skuName = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuName");

        //传入极客云
        VtuzxMap bizcontent = new VtuzxMap();//业务请求参数
        bizcontent.append("goodsNo",materialNo)
                .append("skuName",skuName)
                .append("serialNoList",newTrace)
                .append("snDefine2",VtuzxUtil.formatYmdHmsF(new Date()))
                .append("snDefine1",VtuzxUtil.formatYmdHmsF(new Date()));

            try {
                iJiKeYunApiService.serialNoAdd(storeDoc,bizcontent);
            }
            catch (Exception e){
                throw new VtuzxException(e.getMessage());
            }

        return new VtuzxMap();
    }

    /**
     * 根据唯一码获取信息
     * @param traceBackNo
     * @return
     */
    @Override
    public VtuzxMap getDetailByTrace(String traceBackNo) throws VtuzxException{
        //校验数据
        long count = traceabilityCodeDetailDao.count(Criteria.where("traceabilityCode").is(traceBackNo).and("enableStatus").is(true));
        if (count <= 0){
            throw new VtuzxException("追溯码不存在或还未启用");
        }
        Document traceabilityDoc = traceabilityCodeDetailDao.findOne(Criteria.where("traceabilityCode").is(traceBackNo).and("enableStatus").is(true));
        String boxNo = VtuzxUtil.getString(traceabilityDoc,"boxNo");
        Document prdColDetailDoc = prdColDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
        String billNo = VtuzxUtil.getString(prdColDetailDoc,"billNo");
        Document prdColDoc = prdColDao.findOne(Criteria.where("billNo").is(billNo));
        String materialNo = VtuzxUtil.getString(prdColDoc,"materialNo");
        String materialName = VtuzxUtil.getString(prdColDoc,"materialName");
        String batchNo = VtuzxUtil.getString(prdColDoc,"batchNo");
        String taskBillNo = VtuzxUtil.getString(prdColDoc,"taskBillNo");
        VtuzxMap data = new VtuzxMap();
        data.append("materialNo",materialNo)
                .append("materialName",materialName)
                .append("batchNo",batchNo)
                .append("boxNo",boxNo)
                .append("taskBillNo",taskBillNo);
        return new VtuzxMap("data",data);
    }

    @Override
    public void uploadSaleOut(ObjectId userId, String name, File file) throws Exception {
        createBoxByExcle(userId,name,file);
    }

    @Override
    public VtuzxMap getSerialUp(ObjectId userId, String traceBackNo) throws VtuzxException {
        //判断是批次号还是追溯码
        if(traceBackNo.startsWith("http://")){
            //校验数据
            long count = traceabilityCodeDetailDao.count(Criteria.where("traceabilityCode").is(traceBackNo).and("enableStatus").is(true));
            if (count <= 0){
                throw new VtuzxException("追溯码不存在或还未启用");
            }
            //先获取箱号
            Document inventoryDoc = inventoryDetailDao.findOne(Criteria.where("traceBack.traceBackNo").is(traceBackNo));
            String boxNo = VtuzxUtil.getString(inventoryDoc,"boxNo");
            if (VtuzxUtil.isEmpty(boxNo)){
                throw new VtuzxException("唯一码不存在");
            }
            List<Document> dataList = new ArrayList<>();
            List<Document> list = serialUp(boxNo, dataList);
            dataList = list;
            //整理一下日期字段
            for (Document data : dataList){
                Date createTime = VtuzxUtil.getObject(data,"createTime");
                data.replace("createTime",VtuzxUtil.formatYmdHmsF(createTime));
            }
            return new VtuzxMap("data",dataList);
        } else {
            //先获取箱号
            Document inventoryDoc = inventoryDao.findOne(Criteria.where("batchNo").is(traceBackNo));
            ObjectId _id = VtuzxUtil.getObject(inventoryDoc,"_id");
            Document detailDoc = inventoryDetailDao.findOne(Criteria.where("_inventoryId").is(_id));
            String boxNo = VtuzxUtil.getString(detailDoc,"boxNo");
            if (VtuzxUtil.isEmpty(boxNo)){
                throw new VtuzxException("当前批次不存在");
            }
            List<Document> dataList = new ArrayList<>();
            List<Document> list = serialUp(boxNo, dataList);
            dataList = list;
            //整理一下日期字段
            for (Document data : dataList){
                Date createTime = VtuzxUtil.getObject(data,"createTime");
                data.replace("createTime",VtuzxUtil.formatYmdHmsF(createTime));
            }
            return new VtuzxMap("data",dataList);
        }

    }

    @Override
    public VtuzxMap getTraceabilityCodeList(VtuzxToken token, Map<String, Object> param) {
        String materialNo = VtuzxUtil.getString(param,"materialNo");
        String batchNo = VtuzxUtil.getString(param,"batchNo");
        String traceabilityCode = VtuzxUtil.getString(param,"traceabilityCode");
        Criteria inCond = new Criteria();
        if (!VtuzxUtil.isEmpty(materialNo)){
            inCond.and("materialNo").is(materialNo);
        }
        if (!VtuzxUtil.isEmpty(batchNo)){
            inCond.and("batchNo").regex(batchNo);
        }
        inCond.and("createTime").gte(VtuzxUtil.parseDate("2023-03-01 00:00:00"));
        List<ObjectId> inventoryIdList = new ArrayList<>();
        List<String> serialList = new ArrayList<>();//存放唯一码
        List<String> boxNoList = new ArrayList<>();//存放箱码
        //先根据物料和批号在即时库存外层表查询
        //有批次和物料的话查询即时库存表
        if (!VtuzxUtil.isEmpty(materialNo) || !VtuzxUtil.isEmpty(batchNo)){
            Iterator<Document> inventoryItr = inventoryDao.findList(inCond,null,0,0);
            while (inventoryItr.hasNext()){
                Document inventoryDoc = inventoryItr.next();
                ObjectId _id = VtuzxUtil.getObject(inventoryDoc,"_id");
                if (!inventoryIdList.contains(_id)){
                    inventoryIdList.add(_id);
                }
            }
            //在查询内层表
            List<Document> detailList = new ArrayList<>();
            Criteria detailCond = new Criteria();
            detailCond.and("_inventoryId").in(inventoryIdList);
            detailCond.and("createTime").gte(VtuzxUtil.parseDate("2023-01-01 00:00:00"));
            Iterator<Document> inventoryDetailItr = inventoryDetailDao.findList(detailCond,null,0,0);
            while (inventoryDetailItr.hasNext()){
                Document inventoryDoc = inventoryDetailItr.next();
//                detailList.add(inventoryDoc);
                String boxNo = VtuzxUtil.getString(inventoryDoc,"boxNo");
                if (!boxNoList.contains(boxNo)){
                    boxNoList.add(boxNo);
                }
            }

//            detailList.forEach(item -> {
//                List<Document> traceBack = VtuzxUtil.getObject(item,"traceBack");
//                if (!VtuzxUtil.isEmpty(traceBack)){
//                    traceBack.forEach(trace -> {
//                        String traceBackNo = VtuzxUtil.getString(trace,"traceBackNo");
//                        serialList.add(traceBackNo);
//                    });
//                }
//            } );
        }


        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(traceabilityCode)){
            cond.and("traceabilityCode").regex(traceabilityCode);
        }
        cond.and("lastTime").ne(null);//只取有追溯链的数据
//        if (!VtuzxUtil.isEmpty(serialList)){
//            cond.and("traceabilityCode").in(serialList);
//        }
        if (!VtuzxUtil.isEmpty(boxNoList)){
            cond.and("boxNo").in(boxNoList);
        }
        VtuzxPage page = traceabilityCodeDetailDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("boxNo", VtuzxUtil.getString(doc, "boxNo"))
                .append("traceabilityCode", VtuzxUtil.getString(doc, "traceabilityCode"))
                .append("saleTime", VtuzxUtil.getString(doc, "saleTime"))//销售日期
                .append("allocateTime", VtuzxUtil.getString(doc, "allocateTime"))//调拨日期
                .append("allocateNo", VtuzxUtil.getString(doc, "allocateNo"))//调拨单号
                .append("customerName",VtuzxUtil.getString(doc, "customerName"))//客户名称
                .append("saleChnnel", VtuzxUtil.getString(doc, "saleChnnel"))//吉客云客户名称
                .append("tradeNo", VtuzxUtil.getString(doc, "tradeNo")));//吉客云销售单号);
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    /**
     * 获取成品损耗率的报表
     * @param token
     * @param param
     * @return
     */
    @Override
    public VtuzxMap getLossRoteList(VtuzxToken token, Map<String, Object> param) {
        //先查询任务表数据,在查领料,补料,退料,变品,入库数据,最后计算损耗率
        int pageIndex = VtuzxUtil.getInt(param,"pageIndex");
        int pageSize = VtuzxUtil.getInt(param,"pageSize");
        String taskNo = VtuzxUtil.getString(param,"billNo");
        List<String> createTime = VtuzxUtil.getObject(param,"createTime");
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(createTime)) {
            cond.and("createTime").gte(VtuzxUtil.parseDate(createTime.get(0))).lte(VtuzxUtil.parseDate(createTime.get(1) + " 23:59:59"));
        }
        if (!VtuzxUtil.isEmpty(taskNo)){
            cond.and("billNo").regex(taskNo);
        }
        cond.and("produceHouseName").is("干参包装车间");

        List<String> taskBillNoList = new ArrayList<>();//存放任务号
        VtuzxPage page = prdTaskDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> {
            VtuzxMap data = new VtuzxMap(doc);
            String taskBillNo = VtuzxUtil.getString(data, "billNo");
            if (!taskBillNoList.contains(taskBillNo)){
                taskBillNoList.add(taskBillNo);
            }
            data.append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.removeObject(data, "createTime")));
            return data;
        });


        //查询任务内层表,获取物料信息
//        List<Document> prdTaskDetailList = new ArrayList<>();//任务南内层表数据
//        Iterator<Document> prdTakDetailItr = prdTaskDetailDao.findList(Criteria.where("billNo").in(taskBillNoList),null,0,0);
//        while (prdTakDetailItr.hasNext()){
//            Document detailDoc = prdTakDetailItr.next();
//            prdTaskDetailList.add(detailDoc);
//        }

        //领料补料入库
//        List<Document> lossList = new ArrayList<>();
//        Iterator<Document>  lossItr = prdTaskDao.getTaskLoss(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
//        while (lossItr.hasNext()){
//            Document lossDoc = lossItr.next();
//            Object value = lossDoc.get("allValue");
//
//            System.out.println("");
//        }
        //查询领料信息
        List<Document> materialPickList = new ArrayList<>();
        Iterator<Document> materialPickItr = materialBomPickDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (materialPickItr.hasNext()){
            Document materialPickDoc = materialPickItr.next();
            materialPickList.add(materialPickDoc);
        }
        //查询补料信息
        List<Document> materialSupplimentList = new ArrayList<>();
        Iterator<Document> supplimentItr = materialBomSupplementDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (supplimentItr.hasNext()){
            Document supplimentDoc = supplimentItr.next();
            materialSupplimentList.add(supplimentDoc);
        }
        //查询入库,退料,变品 放到一起查询
        List<Document> prdInList = new ArrayList<>();
        Iterator<Document> prdInItr = prdInDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (prdInItr.hasNext()){
            Document prdInDoc = prdInItr.next();
            prdInList.add(prdInDoc);
        }
        //查询入库详情表,获取入库的物料信息,在根据物料查询克重信息
        List<Document> prdInDetailList = new ArrayList<>();
        List<String> materiaNolList = new ArrayList<>();//存放物料的信息
        Iterator<Document> prdInDetailItr = prdInDetailDao.findList(Criteria.where("taskBillNo").in(taskBillNoList).and("task").is(true),null,0,0);
        while (prdInDetailItr.hasNext()){
            Document prdInDoc = prdInDetailItr.next();
            String materialNo = VtuzxUtil.getString(prdInDoc,"materialNo");
            if (!materiaNolList.contains(materialNo)){
                materiaNolList.add(materialNo);
            }
            prdInDetailList.add(prdInDoc);
        }
        //查询物料表
        List<Document> materialList = new ArrayList<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materiaNolList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            materialList.add(materialDoc);
        }
        //把克重信息添加到入库详情集合中
        for (Document prdInDoc : prdInDetailList){
            String materialNo = VtuzxUtil.getString(prdInDoc,"materialNo");
            for (Document materialDoc : materialList){
                String fNumber = VtuzxUtil.getString(materialDoc,"fNumber");
                if (materialNo.equals(fNumber)){
                    double gramCount = VtuzxUtil.getDouble(materialDoc,"gramCount");
                    prdInDoc.append("gramCount",gramCount);//添加克重信息
                }
            }
        }
        //添加物料信息
//        for (VtuzxMap data : dataList){
//            String billNo = VtuzxUtil.getString(data,"billNo");
//            for (Document detail : prdTaskDetailList){
//                String taskBillNo = VtuzxUtil.getString(detail,"billNo");
//                if (billNo.equals(taskBillNo)){
//                    String materialNo = VtuzxUtil.getString(detail,"materialNo");
//                    String materialName = VtuzxUtil.getString(detail,"materialName");
//                    data.append("materialNo",materialNo)
//                            .append("materialName",materialName);
//                }
//            }
//        }
        //添加领料重量信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document pickDoc : materialPickList){
                String taskBillNo = VtuzxUtil.getString(pickDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double count = VtuzxUtil.getDouble(pickDoc,"count");
                    data.append("pickCount",count);
                }
            }
        }

        //添加补料重量信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document supplimentDoc : materialSupplimentList){
                String taskBillNo = VtuzxUtil.getString(supplimentDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double count = VtuzxUtil.getDouble(supplimentDoc,"count");
                    double supplimentCount = VtuzxUtil.getDouble(data,"supplimentCount");
                    //先判断有没有补料的重量,避免补料有多条的情况
                    if(supplimentCount == 0){
                        data.append("supplimentCount",count);
                    } else {
                        data.append("supplimentCount",count + supplimentCount);
                    }

                }
            }
        }
        //添加退料,变品以及入库信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document prdInDoc : prdInList){
                String taskBillNo = VtuzxUtil.getString(prdInDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    //判断是退料,入库,还是变品
                    boolean task = VtuzxUtil.getBoolean(prdInDoc,"task");//任务入库
                    boolean isReturn = VtuzxUtil.getBoolean(prdInDoc,"return");//退料
                    boolean desalting = VtuzxUtil.getBoolean(prdInDoc,"desalting");//变品
                    double count = VtuzxUtil.getDouble(prdInDoc,"count");
                    if (task){//任务入库
                        //先判断是否有任务入库的数量
                        double taskCount = VtuzxUtil.getDouble(data,"taskCount");
                        if (taskCount == 0){//当前没有入库数
                            data.append("taskCount",count);
                        } else {
                            data.append("taskCount",count + taskCount);
                        }
                    }

                    if (isReturn){//退料
                        //先判断是否有退料的数量
                        double returnCount = VtuzxUtil.getDouble(data,"returnCount");
                        if (returnCount == 0){//当前没有退料
                            data.append("returnCount",count);
                        } else {
                            data.append("returnCount",count + returnCount);
                        }
                    }
                    if (desalting){//变品
                        double desCount = VtuzxUtil.getDouble(data,"desCount");
                        if (desCount == 0){
                            data.append("desCount",count);
                        } else {
                            data.append("desCount",count + desCount);
                        }
                    }
                }
            }
        }
        //添加克重信息到datalist
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document prdInDetail : prdInDetailList){
                String taskBillNo = VtuzxUtil.getString(prdInDetail,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double gramCount = VtuzxUtil.getDouble(prdInDetail,"gramCount");
                    data.append("gramCount",gramCount);
                }
            }
        }
        //计算损耗率
        //第一步：
        //领料数+补料数-退料数-变品数=实际使用数
        //第二步：
        //入库规格*入库数量/500=入库斤数
        //第三步：
        //实际使用数-入库斤数=损耗数
        //第四步：
        //损耗数/实际使用数 *100=损耗率
        DecimalFormat format = new DecimalFormat("#0.00");
        for (VtuzxMap data : dataList){
            double pickCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"pickCount")));//领料数量
            double supplimentCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"supplimentCount")));//补料数量
            double returnCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"returnCount")));//退料数量
            double desCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"desCount")));//变品数量
            double taskCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"taskCount")));//入库数量
            double gramCount = VtuzxUtil.getDouble(data,"gramCount");//克重(规格)
            //计算实际使用数量
            double realityCount = Double.parseDouble(format.format(pickCount + supplimentCount - returnCount - desCount));//实际使用重量
            double taskJin = Double.parseDouble(format.format((gramCount * taskCount) / 500));//入库斤数
            double lossCount = Double.parseDouble(format.format(realityCount - taskJin));//损耗数
            double lossRate = 0;
            if(realityCount == 0){
                lossRate = 0;
            } else {
                lossRate = Double.parseDouble(format.format(lossCount / realityCount *100));//损耗率
            }

            data.append("realityCount",realityCount)
                    .append("taskJin",taskJin)
                    .append("lossCount",lossCount)
                    .append("lossRate",lossRate + "%");
        }
        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxDownloadBean downloadLossRoteList(VtuzxToken token, Map<String, Object> param)throws Exception {
        String taskNo = VtuzxUtil.getString(param,"billNo");
        List<String> createTime = VtuzxUtil.getObject(param,"createTime");
        Criteria cond = new Criteria();

        //数据过多,下载的时候添加上时间
        if (VtuzxUtil.isEmpty(createTime)) {
            throw new VtuzxException("数据过多,请添加时间段");
        }
        DecimalFormat format = new DecimalFormat("#0.00");
        cond.and("createTime").gte(VtuzxUtil.parseDate(createTime.get(0))).lte(VtuzxUtil.parseDate(createTime.get(1) + " 23:59:59"));
        if (!VtuzxUtil.isEmpty(taskNo)){
            cond.and("billNo").regex(taskNo);
        }
        cond.and("produceHouseName").is("干参包装车间");

        List<String> taskBillNoList = new ArrayList<>();//存放任务号
        VtuzxPage page = prdTaskDao.page(cond, new Document("createTime", -1), 0, 0);
        List<VtuzxMap> dataList = page.iterator(doc -> {
            VtuzxMap data = new VtuzxMap(doc);
            String taskBillNo = VtuzxUtil.getString(data, "billNo");
            if (!taskBillNoList.contains(taskBillNo)){
                taskBillNoList.add(taskBillNo);
            }
            data.append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.removeObject(data, "createTime")));
            return data;
        });


        //查询领料信息
        List<Document> materialPickList = new ArrayList<>();
        Iterator<Document> materialPickItr = materialBomPickDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (materialPickItr.hasNext()){
            Document materialPickDoc = materialPickItr.next();
            materialPickList.add(materialPickDoc);
        }
        //查询补料信息
        List<Document> materialSupplimentList = new ArrayList<>();
        Iterator<Document> supplimentItr = materialBomSupplementDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (supplimentItr.hasNext()){
            Document supplimentDoc = supplimentItr.next();
            materialSupplimentList.add(supplimentDoc);
        }
        //查询入库,退料,变品 放到一起查询
        List<Document> prdInList = new ArrayList<>();
        Iterator<Document> prdInItr = prdInDao.findList(Criteria.where("taskBillNo").in(taskBillNoList),null,0,0);
        while (prdInItr.hasNext()){
            Document prdInDoc = prdInItr.next();
            prdInList.add(prdInDoc);
        }
        //查询入库详情表,获取入库的物料信息,在根据物料查询克重信息
        List<Document> prdInDetailList = new ArrayList<>();
        List<String> materiaNolList = new ArrayList<>();//存放物料的信息
        Iterator<Document> prdInDetailItr = prdInDetailDao.findList(Criteria.where("taskBillNo").in(taskBillNoList).and("task").is(true),null,0,0);
        while (prdInDetailItr.hasNext()){
            Document prdInDoc = prdInDetailItr.next();
            String materialNo = VtuzxUtil.getString(prdInDoc,"materialNo");
            if (!materiaNolList.contains(materialNo)){
                materiaNolList.add(materialNo);
            }
            prdInDetailList.add(prdInDoc);
        }
        //查询物料表
        List<Document> materialList = new ArrayList<>();
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materiaNolList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            materialList.add(materialDoc);
        }
        //把克重信息添加到入库详情集合中
        for (Document prdInDoc : prdInDetailList){
            String materialNo = VtuzxUtil.getString(prdInDoc,"materialNo");
            for (Document materialDoc : materialList){
                String fNumber = VtuzxUtil.getString(materialDoc,"fNumber");
                if (materialNo.equals(fNumber)){
                    double gramCount = VtuzxUtil.getDouble(materialDoc,"gramCount");
                    prdInDoc.append("gramCount",gramCount);//添加克重信息
                }
            }
        }

        //添加领料重量信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document pickDoc : materialPickList){
                String taskBillNo = VtuzxUtil.getString(pickDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double count = VtuzxUtil.getDouble(pickDoc,"count");
                    data.append("pickCount",Double.parseDouble(format.format(count)));
                }
            }
        }

        //添加补料重量信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document supplimentDoc : materialSupplimentList){
                String taskBillNo = VtuzxUtil.getString(supplimentDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double count = VtuzxUtil.getDouble(supplimentDoc,"count");
                    double supplimentCount = VtuzxUtil.getDouble(data,"supplimentCount");
                    //先判断有没有补料的重量,避免补料有多条的情况
                    if(supplimentCount == 0){
                        data.append("supplimentCount",Double.parseDouble(format.format(count)));
                    } else {
                        data.append("supplimentCount",Double.parseDouble(format.format(count + supplimentCount)));
                    }

                }
            }
        }
        //添加退料,变品以及入库信息
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document prdInDoc : prdInList){
                String taskBillNo = VtuzxUtil.getString(prdInDoc,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    //判断是退料,入库,还是变品
                    boolean task = VtuzxUtil.getBoolean(prdInDoc,"task");//任务入库
                    boolean isReturn = VtuzxUtil.getBoolean(prdInDoc,"return");//退料
                    boolean desalting = VtuzxUtil.getBoolean(prdInDoc,"desalting");//变品
                    double count = VtuzxUtil.getDouble(prdInDoc,"count");
                    if (task){//任务入库
                        //先判断是否有任务入库的数量
                        double taskCount = VtuzxUtil.getDouble(data,"taskCount");
                        if (taskCount == 0){//当前没有入库数
                            data.append("taskCount",Double.parseDouble(format.format(count)));
                        } else {
                            data.append("taskCount",Double.parseDouble(format.format(count + taskCount)));
                        }
                    }

                    if (isReturn){//退料
                        //先判断是否有退料的数量
                        double returnCount = VtuzxUtil.getDouble(data,"returnCount");
                        if (returnCount == 0){//当前没有退料
                            data.append("returnCount",Double.parseDouble(format.format(count)));
                        } else {
                            data.append("returnCount",Double.parseDouble(format.format(count + returnCount)));
                        }
                    }
                    if (desalting){//变品
                        double desCount = VtuzxUtil.getDouble(data,"desCount");
                        if (desCount == 0){
                            data.append("desCount",Double.parseDouble(format.format(count)));
                        } else {
                            data.append("desCount",Double.parseDouble(format.format(count + desCount)));
                        }
                    }
                }
            }
        }
        //添加克重信息到datalist
        for (VtuzxMap data : dataList){
            String billNo = VtuzxUtil.getString(data,"billNo");
            for (Document prdInDetail : prdInDetailList){
                String taskBillNo = VtuzxUtil.getString(prdInDetail,"taskBillNo");
                if (billNo.equals(taskBillNo)){
                    double gramCount = VtuzxUtil.getDouble(prdInDetail,"gramCount");
                    data.append("gramCount",gramCount);
                }
            }
        }
        //计算损耗率
        //第一步：
        //领料数+补料数-退料数-变品数=实际使用数
        //第二步：
        //入库规格*入库数量/500=入库斤数
        //第三步：
        //实际使用数-入库斤数=损耗数
        //第四步：
        //损耗数/实际使用数 *100=损耗率

        for (VtuzxMap data : dataList){
            double pickCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"pickCount")));//领料数量
            double supplimentCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"supplimentCount")));//补料数量
            double returnCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"returnCount")));//退料数量
            double desCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"desCount")));//变品数量
            double taskCount = Double.parseDouble(format.format(VtuzxUtil.getDouble(data,"taskCount")));//入库数量
            double gramCount = VtuzxUtil.getDouble(data,"gramCount");//克重(规格)
            //计算实际使用数量
            double realityCount = Double.parseDouble(format.format(pickCount + supplimentCount - returnCount - desCount));//实际使用重量
            double taskJin = Double.parseDouble(format.format((gramCount * taskCount) / 500));//入库斤数
            double lossCount = Double.parseDouble(format.format(realityCount - taskJin));//损耗数
            double lossRate = 0;
            if(realityCount == 0){
                lossRate = 0;
            } else {
                lossRate = Double.parseDouble(format.format(lossCount / realityCount *100));//损耗率
            }

            data.append("realityCount",realityCount)
                    .append("taskJin",taskJin)
                    .append("lossCount",lossCount)
                    .append("lossRate",lossRate + "%");
        }

        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "单据编号"),
                new VtuzxExcelCell(col++, "生产车间"),
                new VtuzxExcelCell(col++, "数量"),
                new VtuzxExcelCell(col++, "创建人"),
                new VtuzxExcelCell(col++, "创建日期"),
                new VtuzxExcelCell(col++, "领料数量"),
                new VtuzxExcelCell(col++, "补料数量"),
                new VtuzxExcelCell(col++, "退料数量"),
                new VtuzxExcelCell(col++, "变品数量"),
                new VtuzxExcelCell(col++, "入库数量"),
                new VtuzxExcelCell(col++, "实际使用数量"),
                new VtuzxExcelCell(col++, "入库规格(克重)"),
                new VtuzxExcelCell(col++, "入库斤数"),
                new VtuzxExcelCell(col++, "损耗数"),
                new VtuzxExcelCell(col, "损耗率")
        ));
        sheet0.addRow(header0);

        for (VtuzxMap detail : dataList) {
//            VtuzxMap header = VtuzxUtil.getObject(detail, "header");
//            List<VtuzxMap> boxList = VtuzxUtil.getObject(detail, "boxList");

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "produceHouseName")),
                    new VtuzxExcelCell(col++, new BigDecimal(VtuzxUtil.getString(detail, "count")).setScale(2, RoundingMode.HALF_UP).doubleValue()),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "createUserName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "createTime")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "pickCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "supplimentCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "returnCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "desCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "taskCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "realityCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "gramCount")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "taskJin")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "lossCount")),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "lossRate"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 300},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 150},
                new int[]{7, 150},
                new int[]{8, 100},
                new int[]{9, 100},
                new int[]{10, 100},
                new int[]{11, 100},
                new int[]{12, 100},
                new int[]{13, 100},
                new int[]{14, 100},
                new int[]{15, 100}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("损耗率报表" + timestamp + ".xlsx", stream);
    }

    @Override
    public VtuzxMap getBatchNoUp(ObjectId userId, String batchNo) throws Exception{
        //先获取箱号
        Document inventoryDoc = inventoryDao.findOne(Criteria.where("batchNo").is(batchNo));
        ObjectId _id = VtuzxUtil.getObject(inventoryDoc,"_id");
        Document detailDoc = inventoryDetailDao.findOne(Criteria.where("_inventoryId").is(_id));
        String boxNo = VtuzxUtil.getString(detailDoc,"boxNo");
        if (VtuzxUtil.isEmpty(boxNo)){
            throw new VtuzxException("当前批次不存在");
        }
        List<Document> dataList = new ArrayList<>();
        List<Document> list = serialUp(boxNo, dataList);
        dataList = list;
        //整理一下日期字段
        for (Document data : dataList){
            Date createTime = VtuzxUtil.getObject(data,"createTime");
            data.replace("createTime",VtuzxUtil.formatYmdHmsF(createTime));
        }
        return new VtuzxMap("data",dataList);

    }

    @Override
    public VtuzxMap getRegular(VtuzxToken token, Map<String, Object> param) {
        int pageIndex = VtuzxUtil.getInt(param, "pageIndex");
        int pageSize = VtuzxUtil.getInt(param, "pageSize");
        String stockNo = VtuzxUtil.getString(param, "stockNo");
        List<String> createTime = new ArrayList<>();
        Criteria cond = new Criteria();
        if (!VtuzxUtil.isEmpty(stockNo)){
            cond.and("stockNo").is(stockNo);
        }
        if (!VtuzxUtil.isEmpty(createTime)){
            cond.and("createTime").gte(VtuzxUtil.parseDate(createTime.get(0))).lte(VtuzxUtil.parseDate(createTime.get(1) + " 23:59:59"));
        }
        VtuzxPage page = inventoryRegularDao.page(cond, new Document("createTime", -1), pageIndex, pageSize);
        List<VtuzxMap> dataList = page.iterator(doc -> new VtuzxMap("billNo", VtuzxUtil.getString(doc, "billNo"))
                .append("createTime", VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(doc, "createTime")))
                .append("stockName", VtuzxUtil.getString(doc, "stockName"))
                .append("stockNo", VtuzxUtil.getString(doc, "stockNo")));


        return new VtuzxMap(IVtuzxConst.Key_total, page.total()).append(IVtuzxConst.Key_data, dataList);
    }

    @Override
    public VtuzxDownloadBean downloadRegular(VtuzxToken token, Map<String, Object> param) throws Exception{
        String billNo = VtuzxUtil.getString(param,"billNo");
        Iterator<Document> regularItr = inventoryRegularDetailDao.findList(Criteria.where("billNo").is(billNo), null, 0, 0);
        List<Document> dataList = new ArrayList<>();
        while (regularItr.hasNext()){
            Document regularDoc = regularItr.next();
            dataList.add(regularDoc);
        }
        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "定时库存");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "仓库"),
                new VtuzxExcelCell(col++, "仓位"),
                new VtuzxExcelCell(col++, "物料编码"),
                new VtuzxExcelCell(col++, "物料名称"),
                new VtuzxExcelCell(col++, "物料规格"),
                new VtuzxExcelCell(col++, "批次号"),
                new VtuzxExcelCell(col++, "库存箱数"),
                new VtuzxExcelCell(col++, "库存总量"),
                new VtuzxExcelCell(col, "单位")
        ));
        sheet0.addRow(header0);

        for (Document detail : dataList) {
//            VtuzxMap header = VtuzxUtil.getObject(detail, "header");
//            List<VtuzxMap> boxList = VtuzxUtil.getObject(detail, "boxList");

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "locationNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialSpec")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxCount")),
                    new VtuzxExcelCell(col++, new BigDecimal(VtuzxUtil.getString(detail, "count")).setScale(2, RoundingMode.HALF_UP).doubleValue()),
                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "unit"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 300},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 150},
                new int[]{7, 150},
                new int[]{8, 80}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("定时库存" + timestamp + ".xlsx", stream);
    }

    /**
     * 飞书获取原料库存
     * @param token
     * @param param
     * @return
     */
    @Override
    public VtuzxMap getInventoryFeiShu(VtuzxToken token, Map<String, Object> param) {
        List<String> stockNoList = new ArrayList<>();//查询的仓库编号
        stockNoList.add("001");//原料二号库
        stockNoList.add("010");//原料三
        stockNoList.add("012");//原料5
        stockNoList.add("023");//原料7
        stockNoList.add("002");//干参半成品二号库
        stockNoList.add("006");//干参半成品一号库
        stockNoList.add("017");//干参半成品四号库
        stockNoList.add("019");//干参半成品三号库
        stockNoList.add("003");//即食海参半成品12
        stockNoList.add("004");//即食海参半成品8
        stockNoList.add("005");//即食海参半成品6
        Criteria cond = new Criteria();
        cond.and("stockNo").in(stockNoList).and("count").gt(0);
        VtuzxMap stockMap = new VtuzxMap();//key:仓库编号，val名称
        Iterator<Document> stockItr = stockDao.findList(Criteria.where("fNumber").in(stockNoList),null,0,0);
        while (stockItr.hasNext()){
            Document stockDoc = stockItr.next();
            String stockNo = VtuzxUtil.getString(stockDoc,"fNumber");
            String stockName = VtuzxUtil.getString(stockDoc,"fName");
            stockMap.append(stockNo,stockName);
        }
        Iterator<Document> inventoryDaoRawMaterial = inventoryDao.findRawMaterial(cond,null,0,0);
        List<VtuzxMap> dataList = new ArrayList<>();
        List<String> materialNoList = new ArrayList<>();//物料编码列表
        while (inventoryDaoRawMaterial.hasNext()){
            Document inventoryDoc = inventoryDaoRawMaterial.next();
            double count = VtuzxUtil.getDouble(inventoryDoc,"count");//总数量
            Document material = VtuzxUtil.getObject(inventoryDoc,"_id");//物料编码和仓库编码
            String materialNo = VtuzxUtil.getString(material,"materialNo");
            if (!materialNoList.contains(materialNo)){
                materialNoList.add(materialNo);
            }
            String stockNo = VtuzxUtil.getString(material,"stockNo");
            String stockName = VtuzxUtil.getString(stockMap,stockNo);
            VtuzxMap data = new VtuzxMap();
            data.append("materialNo",materialNo)
                    .append("stockName",stockName)
                    .append("count",String.format("%.2f",count));
            dataList.add(data);
        }
        VtuzxMap materialMap = new VtuzxMap();//key物料编码 val名称和规格
        Iterator<Document> materialItr = materialDao.findList(Criteria.where("fNumber").in(materialNoList),null,0,0);
        while (materialItr.hasNext()){
            Document materialDoc = materialItr.next();
            String materialNo = VtuzxUtil.getString(materialDoc,"fNumber");
            String materialName = VtuzxUtil.getString(materialDoc,"fName");
            String specification = VtuzxUtil.getString(materialDoc,"fSpecification");
            VtuzxMap material = new VtuzxMap();//名称和规格
            material.append("materialName",materialName)
                    .append("specification",specification);
            materialMap.append(materialNo,material);
        }
        for (VtuzxMap data : dataList){
            String materialNo = VtuzxUtil.getString(data,"materialNo");
            VtuzxMap material = VtuzxUtil.getObject(materialMap,materialNo);
            String materialName = VtuzxUtil.getString(material,"materialName");
            String specification = VtuzxUtil.getString(material,"specification");
            data.append("specification",specification)
                    .append("materialName",materialName);
        }
        VtuzxMap data = new VtuzxMap();
        data.append("dataList",dataList);
        return data;
    }

    @Override
    public VtuzxMap getSerialTrade(ObjectId userId, String traceBackNo) throws Exception {
        //判断传过来的是唯一码，批号，还是生产任务号
        List<String> serialNoList = new ArrayList<>();
        if (traceBackNo.startsWith("SCRW")){//任务号
            Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("taskBillNo")
                    .is(traceBackNo),null,0,0);
            while (prdColDetailItr.hasNext()){
                Document prdColDetailDoc = prdColDetailItr.next();
                List<String> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                if (!VtuzxUtil.isEmpty(traceBack)){
                    serialNoList.addAll(traceBack);
                }
            }
        } else if(traceBackNo.startsWith("Z")){//唯一码
            String serialNo = "http://zs.xiaoqin.com.cn/xqc_h5?traceabilityCode=" + traceBackNo;
            serialNoList.add(serialNo);
        } else {//批次号
            List<String> billNoList = new ArrayList<>();
            Iterator<Document> prdColItr =  prdColDao.findList(Criteria.where("colType").is("saveTask")
                    .and("batchNo").is(traceBackNo),null,0,0);
            while (prdColItr.hasNext()){
                Document prdColDoc = prdColItr.next();
                String billNo = VtuzxUtil.getString(prdColDoc,"billNo");
                if (!billNoList.contains(billNo)){
                    billNoList.add(billNo);
                }
            }
            Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("billNo")
                    .in(billNoList),null,0,0);
            while (prdColDetailItr.hasNext()){
                Document prdColDetailDoc = prdColDetailItr.next();
                List<String> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                if (!VtuzxUtil.isEmpty(traceBack)){
                    serialNoList.addAll(traceBack);
                }
            }
        }
        List<Document> traceCodeList = new ArrayList<>();
        //根据唯一码查询信息
        Iterator<Document> tracebilityCodeItr = traceabilityCodeDetailDao.findList(Criteria.where("traceabilityCode")
                .in(serialNoList),null,0,0);
        while (tracebilityCodeItr.hasNext()){
            Document traceDoc = tracebilityCodeItr.next();
            traceCodeList.add(traceDoc);
        }
        VtuzxMap data = new VtuzxMap();
        data.put("dataList",traceCodeList);

        return data;
    }

    /**
     * 下载追溯上查信息
     * @param token
     * @param traceBackNo
     * @return
     * @throws Exception
     */
    @Override
    public VtuzxDownloadBean downloadSerialTrade(VtuzxToken token, String traceBackNo) throws Exception {
        //判断传过来的是唯一码，批号，还是生产任务号
        List<String> serialNoList = new ArrayList<>();
        if (traceBackNo.startsWith("SCRW")){//任务号
            Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("taskBillNo")
                    .is(traceBackNo),null,0,0);
            while (prdColDetailItr.hasNext()){
                Document prdColDetailDoc = prdColDetailItr.next();
                List<String> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                if (!VtuzxUtil.isEmpty(traceBack)){
                    serialNoList.addAll(traceBack);
                }
            }
        } else if(traceBackNo.startsWith("Z")){//唯一码
            String serialNo = "http://zs.xiaoqin.com.cn/xqc_h5?traceabilityCode=" + traceBackNo;
            serialNoList.add(serialNo);
        } else {//批次号
            List<String> billNoList = new ArrayList<>();
            Iterator<Document> prdColItr =  prdColDao.findList(Criteria.where("colType").is("saveTask")
                    .and("batchNo").is(traceBackNo),null,0,0);
            while (prdColItr.hasNext()){
                Document prdColDoc = prdColItr.next();
                String billNo = VtuzxUtil.getString(prdColDoc,"billNo");
                if (!billNoList.contains(billNo)){
                    billNoList.add(billNo);
                }
            }
            Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("billNo")
                    .in(billNoList),null,0,0);
            while (prdColDetailItr.hasNext()){
                Document prdColDetailDoc = prdColDetailItr.next();
                List<String> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                if (!VtuzxUtil.isEmpty(traceBack)){
                    serialNoList.addAll(traceBack);
                }
            }
        }
        List<Document> traceCodeList = new ArrayList<>();
        //根据唯一码查询信息
        Iterator<Document> tracebilityCodeItr = traceabilityCodeDetailDao.findList(Criteria.where("traceabilityCode")
                .in(serialNoList),null,0,0);
        while (tracebilityCodeItr.hasNext()){
            Document traceDoc = tracebilityCodeItr.next();
            traceCodeList.add(traceDoc);
        }


        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());

        List<VtuzxExcelSheet> sheets = new ArrayList<>();
        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "追溯下查");
        int col = 0;
        int row = 0;
        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                new VtuzxExcelCell(col++, "箱号"),
                new VtuzxExcelCell(col++, "销售渠道"),
                new VtuzxExcelCell(col++, "销售时间"),
                new VtuzxExcelCell(col++, "销售单号"),
                new VtuzxExcelCell(col++, "接收方"),
                new VtuzxExcelCell(col++, "发货方"),
                new VtuzxExcelCell(col++, "调拨单号"),
                new VtuzxExcelCell(col++, "调拨时间"),
                new VtuzxExcelCell(col, "唯一码")
        ));
        sheet0.addRow(header0);

        for (Document detail : traceCodeList) {
//            VtuzxMap header = VtuzxUtil.getObject(detail, "header");
//            List<VtuzxMap> boxList = VtuzxUtil.getObject(detail, "boxList");

            col = 0;
            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "saleChnnel")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "saleTime")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "tradeNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "receive")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "issue")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "allocateNo")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "allocateTime")),
                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "traceabilityCode"))
            ));
            sheet0.addRow(dataRow);
        }

        sheet0.setColumnWidth(new VtuzxArrayList<>(
                new int[]{0, 200},
                new int[]{1, 200},
                new int[]{2, 200},
                new int[]{3, 200},
                new int[]{4, 200},
                new int[]{5, 200},
                new int[]{6, 200},
                new int[]{7, 200},
                new int[]{8, 300}
        ));

        sheets.add(sheet0);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VtuzxExcel.write(stream, sheets);
        return new VtuzxDownloadBean("追溯下查" + timestamp + ".xlsx", stream);


    }

    //追溯上查
    private List<Document> serialUp(String boxNo,List<Document> dataList){

        if (VtuzxUtil.isEmpty(boxNo)){
            return dataList;
        }
        //先查入库
        Document prdInDoc = prdInDetailBoxDao.findOne(Criteria.where("boxNo").is(boxNo));
        if (!VtuzxUtil.isEmpty(prdInDoc)){
            //添加入库信息
            dataList.add(prdInDoc);
            //获取任务单号
            String taskBillNo = VtuzxUtil.getString(prdInDoc,"taskBillNo");
            //存在任务单号为空的情况先查询入库单号
            if(VtuzxUtil.isEmpty(taskBillNo)){
                String prdInBillNo = VtuzxUtil.getString(prdInDoc,"billNo");
                Document prdDoc = prdInDao.findOne(Criteria.where("billNo").is(prdInBillNo));
                taskBillNo = VtuzxUtil.getString(prdDoc,"taskBillNo");
            }
            //查询领料信息
            Document materialPickDoc = materialBomPickDetailBoxDao.findOne(Criteria.where("taskBillNo").is(taskBillNo));
            //获取领料的箱号
            if(!VtuzxUtil.isEmpty(materialPickDoc)){
                String pickBoxNo = VtuzxUtil.getString(materialPickDoc,"boxNo");
                //添加领料信息
                dataList.add(materialPickDoc);
                List<Document> list = serialUp(pickBoxNo, dataList);
                dataList = list;
            }



        } else {//如果入库单没有对应箱号信息先查其他入库
            Document otherInDoc = otherInDetailBoxDao.findOne(Criteria.where("boxNo").is(boxNo));
            if (!VtuzxUtil.isEmpty(otherInDoc)){//如果其他入库有数据返回
                //添加其他入库信息
                dataList.add(otherInDoc);
                List<Document> list = serialUp(null, dataList);
                dataList = list;
            } else {//其他入库没有数据，查询采购入库
                Document purInDoc = purInDetailBoxDao.findOne(Criteria.where("boxNo").is(boxNo));
                if (!VtuzxUtil.isEmpty(purInDoc)){
                    //添加采购信息
                    dataList.add(purInDoc);
                    List<Document> list  = serialUp(null, dataList);
                    dataList = list;
                }
            }
        }
        return dataList;
    }

    //销售出唯一码
    private void uploadserialOut(ObjectId userId, String name, File file)throws Exception{
        List<VtuzxExcelSheet> sheets = VtuzxExcel.read(file);
        VtuzxExcelSheet sheet = sheets.get(0);
        int rowSize = sheet.rowSize();
        int count = rowSize / 50;
        for (int j = 0; j < count;j++){
            System.out.println(j);
            Map<String,List<Document>> map = new HashMap<>();//k 是仓库，v 对应唯一码
            for (int i = j * 50 ; i < j * 50 + 50; i++){
                String serialNo = sheet.row(i).cell(0).strCellValue();
                String boxNo = sheet.row(i).cell(1).strCellValue();
                String stockNo = sheet.row(i).cell(2).strCellValue();//仓库
                List<Document> boxMapList = map.get(stockNo);
                if (!VtuzxUtil.isEmpty(boxMapList)){
                    Document doc = new Document();
                    doc.append("traceBack",serialNo).append("boxNo",boxNo);
                    boxMapList.add(doc);
                    map.put(stockNo,boxMapList);
                } else {
                    List<Document> newList = new ArrayList<>();//如果没有的话
                    Document doc = new Document();
                    doc.append("traceBack",serialNo).append("boxNo",boxNo);
                    newList.add(doc);
                    map.put(stockNo,newList);
                }
            }
            for (Map.Entry entry : map.entrySet()){
                reduceInventory(userId,"name",BillEvent.Audit,BillType.XSCK,(String) entry.getKey(),"billNo",null,null,(List<Document>) entry.getValue());
            }
        }
    }

    //修改唯一码状态
    private void updateSerialState(ObjectId userId, String name, File file)throws Exception{
        List<VtuzxExcelSheet> sheets = VtuzxExcel.read(file);
        VtuzxExcelSheet sheet = sheets.get(0);
        int rowSize = sheet.rowSize();
        int count = rowSize / 50;
        for (int j = 0; j < count;j++){
            System.out.println(j);
            List<String> serialList = new ArrayList<>();
            for (int i = j * 50 ; i < j * 50 + 50; i++){
                String serialNo = sheet.row(i).cell(0).strCellValue();
                serialList.add(serialNo);
                traceabilityCodeDetailDao.updateMany(Criteria.where("traceabilityCode").in(serialList),new Document("enableStatus",true));
            }

        }
    }

    //读取excle传吉客云箱号
    private void createBoxByExcle(ObjectId userId, String name, File file)throws Exception {
        List<VtuzxExcelSheet> sheets = VtuzxExcel.read(file);
        VtuzxExcelSheet sheet = sheets.get(0);
        int rowSize = sheet.rowSize();
        int count = rowSize / 50;

//        for (int j = 0; j < count; j++) {
            List<String> boxNoList = new ArrayList<>();
//            System.out.println(j);
            for (int i = 0; i < rowSize; i++) {
                String boxNoKey = sheet.row(i).cell(0).strCellValue();
                String[] boxValues = boxNoKey.split("\\|\\;");
                String boxNo = boxValues[boxValues.length - 1];
                boxNoList.add(boxNo);
            }
            Criteria cond = new Criteria();
            cond.and("boxNo").in(boxNoList);
            Iterator<Document> inventoryItr = inventoryDetailDao.findList(cond, new Document("createTime", -1), 0, 0);
            List<VtuzxMap> inventoryList = new ArrayList<>();
            int size = 0;
            while (inventoryItr.hasNext()) {
                size++;
                Document inventoryDoc = inventoryItr.next();
                VtuzxMap inventoryMap = new VtuzxMap(inventoryDoc);
                inventoryList.add(inventoryMap);
            }
            BillType billType = BillType.SCRK;
            //获取令牌
            Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
            for (VtuzxMap invMap : inventoryList) {
                String boxNo = VtuzxUtil.getString(invMap, "boxNo");
                List<Document> traceBack = VtuzxUtil.getObject(invMap, "traceBack");
                Document prdColDoc = prdColDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
                String batchNo = VtuzxUtil.getString(prdColDoc, "batchNo");

                double colCount = VtuzxUtil.getDouble(prdColDoc, "count");
                String id = VtuzxUtil.getString(prdColDoc, "_id");
                String inventoryId = VtuzxUtil.getString(invMap, "_inventoryId");
                //从外层表中取出物料信息
                Document invenDoc = inventoryDao.findById(inventoryId);
                String materialNo = VtuzxUtil.getString(invenDoc, "materialNo");
                List<VtuzxMap> boxSerialList = new ArrayList<>();//箱子接口唯一码参数
                List<String> traceBackList = new ArrayList<>();//存储整理后的追溯码
                for (Document trace : traceBack) {
                    String traceNo = VtuzxUtil.getString(trace, "traceBackNo");
                    traceBackList.add(traceNo);
                    VtuzxMap traceMap = new VtuzxMap("sn", traceNo);
                    boxSerialList.add(traceMap);

                }


                VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
                //需要先获取条码(skubarcode)
                VtuzxMap goodsParam = new VtuzxMap();
                goodsParam.append("pageIndex", 0)
                        .append("pageSize", 1)
                        .append("goodsNo", materialNo);
                try {
                    goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
                } catch (Exception e) {
                    throw new VtuzxException(e.getMessage());
                }

                LinkedHashMap result = VtuzxUtil.getObject(goodsMap, "result");
                LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result, "data");
                List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data, "goods");
                LinkedHashMap goodsResult = goods.get(0);
                String skuBarcode = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuBarcode");
                String skuId = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuId");
                //传入唯一码
                //查询规格
                Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNo));
                String materialName = VtuzxUtil.getString(materialDoc, "fName");
                //创建箱子
                VtuzxMap boxParam = new VtuzxMap();//箱码总参数
                List<VtuzxMap> boxDetailList = new ArrayList<>();//入库申请单明细

                int serialCount = boxSerialList.size();//每个箱子的唯一码数量
                boxDetailList.add(new VtuzxMap("goodsNo", materialNo)
                        .append("goodsName", materialName)
                        .append("skuId", skuId)
                        .append("boxCount", serialCount)
                        .append("snList", boxSerialList));
                boxParam.append("boxCount", 1)
                        .append("boxDetailList", boxDetailList)
                        .append("isAuto", 3)
                        .append("warehouseName","")
                        .append("warehouseCode","")
                        .append("boxNo", billType.barcodeKey() + "|;" + id + "|;" + materialNo + "|;" + "888888" + "|;" + colCount + "|;" + boxNo);


                try {
                    iJiKeYunApiService.createBoxBatch(storeDoc, boxParam);
                } catch (Exception e) {
                    throw new VtuzxException(e.getMessage());
                }
            }
//        }

    }

    //传入箱码
    private void createBox() throws Exception{
//        String billNo = "CJRK231110082357844";
        Criteria cond = new Criteria();
        String prdInBillNo = "CJRK250902073046491";
        Iterator<Document> prdInItr = prdInDetailBoxDao.findList(Criteria.where("billNo").is(prdInBillNo), null, 0, 0);
        List<String> boxList = new ArrayList<>();
        while (prdInItr.hasNext()) {
            Document prdInDoc = prdInItr.next();
            String boxNo = VtuzxUtil.getString(prdInDoc, "boxNo");
            boxList.add(boxNo);
        }

//        List<String> boxList = new ArrayList<>();
//        boxList.add("A021174338242638");
//        boxList.add("A021174312615363");


        cond.and("boxNo").in(boxList);
        Iterator<Document> inventoryItr = inventoryDetailDao.findList(cond, new Document("createTime", -1), 0, 0);
        List<VtuzxMap> inventoryList = new ArrayList<>();
        int size = 0;
        while (inventoryItr.hasNext()) {
            size++;
            Document inventoryDoc = inventoryItr.next();
            VtuzxMap inventoryMap = new VtuzxMap(inventoryDoc);
            inventoryList.add(inventoryMap);
        }
        BillType billType = BillType.SCRK;
        //获取令牌
        Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
        for (VtuzxMap invMap : inventoryList) {
            String boxNo = VtuzxUtil.getString(invMap, "boxNo");
            List<Document> traceBack = VtuzxUtil.getObject(invMap, "traceBack");
            Document prdColDoc = prdColDetailDao.findOne(Criteria.where("boxNo").is(boxNo));
            String batchNo = VtuzxUtil.getString(prdColDoc,"batchNo");
//            String batchNo = oldBatchNo.substring(0,7);
//            String batchNo = "";
//            if (oldBatchNo.startsWith("B")){
//               String batchNo  = oldBatchNo.substring(1,oldBatchNo.length());
//            } else {
//                batchNo = oldBatchNo;
//            }


            double count = VtuzxUtil.getDouble(prdColDoc,"count");
            String id = VtuzxUtil.getString(prdColDoc,"_id");
            String inventoryId = VtuzxUtil.getString(invMap, "_inventoryId");
            //从外层表中取出物料信息
            Document invenDoc = inventoryDao.findById(inventoryId);
            String materialNo = VtuzxUtil.getString(invenDoc, "materialNo");
            List<VtuzxMap> boxSerialList = new ArrayList<>();//箱子接口唯一码参数
            List<String> traceBackList = new ArrayList<>();//存储整理后的追溯码
            for (Document trace : traceBack) {
                String traceNo = VtuzxUtil.getString(trace, "traceBackNo");
                traceBackList.add(traceNo);
                VtuzxMap traceMap = new VtuzxMap("sn", traceNo);
                boxSerialList.add(traceMap);

            }


            VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
            //需要先获取条码(skubarcode)
            VtuzxMap goodsParam = new VtuzxMap();
            goodsParam.append("pageIndex", 0)
                    .append("pageSize", 1)
                    .append("goodsNo", materialNo);
            try {
                goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
            } catch (Exception e) {
                throw new VtuzxException(e.getMessage());
            }

            LinkedHashMap result = VtuzxUtil.getObject(goodsMap, "result");
            LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result, "data");
            List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data, "goods");
            LinkedHashMap goodsResult = goods.get(0);
            String skuBarcode = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuBarcode");
            String skuId = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuId");
            //传入唯一码
            //查询规格
            Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNo));
            String materialModel = VtuzxUtil.getString(materialDoc, "fSpecification");
            String materialName = VtuzxUtil.getString(materialDoc, "fName");
            //创建箱子
            VtuzxMap boxParam = new VtuzxMap();//箱码总参数
            List<VtuzxMap> boxDetailList = new ArrayList<>();//入库申请单明细

            int serialCount = boxSerialList.size();//每个箱子的唯一码数量
            boxDetailList.add(new VtuzxMap("goodsNo", materialNo)
                    .append("goodsName", materialName)
                    .append("skuId", skuId)
                    .append("boxCount", serialCount)
                    .append("snList", boxSerialList));
            boxParam.append("boxCount", 1)
                    .append("boxDetailList", boxDetailList)
                    .append("isAuto", 3)
                    .append("boxNo", billType.barcodeKey() + "|;" + id + "|;" + materialNo + "|;" + "888888" + "|;" + count +"|;" +boxNo);


            try {
                iJiKeYunApiService.createBoxBatch(storeDoc, boxParam);
            } catch (Exception e) {
                throw new VtuzxException(e.getMessage());
            }


        }
    }

    //传吉客云唯一码
    private void createSerial() throws Exception {

        List<String> prdInBillNoList = new ArrayList<>();
        prdInBillNoList.add("CJRK250902073046491");



        for (String prdInBillNo : prdInBillNoList){
            Criteria cond = new Criteria();
            Iterator<Document> prdInItr = prdInDetailBoxDao.findList(Criteria.where("billNo").is(prdInBillNo), null, 0, 0);
            List<String> boxList = new ArrayList<>();
//        boxList.add("A021691814828282");
            while (prdInItr.hasNext()) {
                Document prdInDoc = prdInItr.next();
                String boxNo = VtuzxUtil.getString(prdInDoc, "boxNo");
                boxList.add(boxNo);
            }
            cond.and("boxNo").in(boxList);
            Iterator<Document> inventoryItr = inventoryDetailDao.findList(cond, new Document("createTime", -1), 0, 0);
            List<VtuzxMap> inventoryList = new ArrayList<>();
            int size = 0;
            while (inventoryItr.hasNext()) {
                size++;
                Document inventoryDoc = inventoryItr.next();
                List<VtuzxMap> trac = null;
                try {
                    trac = VtuzxUtil.getObject(inventoryDoc, "traceBack");
                } catch (ClassCastException e) {
                    e.printStackTrace();
                }

                if (trac != null && trac.size() > 0) {
                    VtuzxMap inventoryMap = new VtuzxMap(inventoryDoc);
                    inventoryList.add(inventoryMap);
                }
            }
            BillType billType = BillType.SCRK;
            //获取令牌
            Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
            for (VtuzxMap invMap : inventoryList) {
                String boxNo = VtuzxUtil.getString(invMap, "boxNo");

                List<Document> traceBack = VtuzxUtil.getObject(invMap, "traceBack");
                String createTime = VtuzxUtil.formatYmdHmsF(VtuzxUtil.getObject(invMap, "createTime"));

                String inventoryId = VtuzxUtil.getString(invMap, "_inventoryId");
                //从外层表中取出物料信息
                Document invenDoc = inventoryDao.findById(inventoryId);
                String materialNo = VtuzxUtil.getString(invenDoc, "materialNo");
                if ("04030307010".equals(materialNo)) {
                    materialNo = "04030307009";
                }
                List<VtuzxMap> boxSerialList = new ArrayList<>();//箱子接口唯一码参数
                List<String> traceBackList = new ArrayList<>();//存储整理后的追溯码
                for (Document trace : traceBack) {

                    String traceNo = VtuzxUtil.getString(trace, "traceBackNo");
                    traceBackList.add(traceNo);
                    VtuzxMap traceMap = new VtuzxMap("sn", traceNo);
                    boxSerialList.add(traceMap);

                }
                //把唯一码整理成string类型的
                String stringFromList = String.join(",", traceBackList);
                VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
                //需要先获取条码(skubarcode)
                VtuzxMap goodsParam = new VtuzxMap();
                goodsParam.append("pageIndex", 0)
                        .append("pageSize", 1)
                        .append("goodsNo", materialNo);
                try {
                    goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
                } catch (Exception e) {
                    throw new VtuzxException(e.getMessage());
                }

                LinkedHashMap result = VtuzxUtil.getObject(goodsMap, "result");
                LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result, "data");
                List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data, "goods");
                if (goods != null && goods.size() > 0) {
                    LinkedHashMap goodsResult = goods.get(0);
                    String materialModel = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuName");
                    String skuId = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuId");
                    //传入唯一码
                    //查询规格
                    Document materialDoc = materialDao.findOne(Criteria.where("fNumber").is(materialNo));
                    String materialName = VtuzxUtil.getString(materialDoc, "fName");
                    VtuzxMap bizcontent = new VtuzxMap();//业务请求参数
                    System.out.println(stringFromList);
                    bizcontent.append("goodsNo", materialNo)
                            .append("skuName", materialModel)
                            .append("serialNoList", stringFromList)
                            .append("snDefine1", VtuzxUtil.formatYmdHmsF(new Date()))
                            .append("snDefine2", createTime);//生产日期
                    if (!VtuzxUtil.isEmpty(stringFromList)) {
                        try {
                            iJiKeYunApiService.serialNoAdd(storeDoc, bizcontent);
                        } catch (Exception e) {
                            _logger.info(e.getMessage());
                        }
                    }
                }

            }
        }

        }


    //传采购入库单
    private void createPrdIn() throws Exception{
        String billNo = "CJRK250331091527274";
        Iterator<Document> purInDetailItr = purInDetailDao.findList(Criteria.where("billNo").is(billNo),null,0,0);
        List<VtuzxMap> materialNoList = new ArrayList<>();
        while (purInDetailItr.hasNext()){
            Document purInDetailDoc = purInDetailItr.next();
            String materialNo = VtuzxUtil.getString(purInDetailDoc,"materialNo");
            double inCount = VtuzxUtil.getDouble(purInDetailDoc,"count");
            if (materialNo.startsWith("03") || materialNo.startsWith("04") || materialNo.startsWith("05") || materialNo.startsWith("06") || materialNo.startsWith("09")){
                VtuzxMap materialMap = new VtuzxMap();
                materialMap.append("materialNo",materialNo)
                        .append("inCount",inCount);
                materialNoList.add(materialMap);
            }
        }
        String intWarehouseCode = "XQWG00101";//仓库编码

        //获取令牌
        Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
        //创建极客云入库单
        VtuzxMap stockParam = new VtuzxMap();//入库参数
        List<VtuzxMap> stockInDetailViews = new ArrayList<>();//入库申请单明细
        for (VtuzxMap material:materialNoList){
            String materialNo = VtuzxUtil.getString(material,"materialNo");
            double inCount = VtuzxUtil.getDouble(material,"inCount");
            VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
            //需要先获取条码(skubarcode)
            VtuzxMap goodsParam = new VtuzxMap();
            goodsParam.append("pageIndex",0)
                    .append("pageSize",1)
                    .append("goodsNo",materialNo);
            try {
                goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
            } catch (Exception e){
                throw new VtuzxException(e.getMessage());
            }
            LinkedHashMap result = VtuzxUtil.getObject(goodsMap,"result");
            LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result,"data");
            List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data,"goods");
            LinkedHashMap goodsResult = null;
            if (goods.size()>0){
                goodsResult = goods.get(0);
            }
            String skuBarcode = VtuzxUtil.getString((Map<String, Object>) goodsResult,"skuBarcode");//条码
            VtuzxMap stockInMap = new VtuzxMap();
            stockInMap.append("relDetailId",billNo)
                    .append("skuCount",inCount)
                    .append("isCertified",1)
                    .append("skuBarcode",skuBarcode);
            stockInDetailViews.add(stockInMap);
        }
        stockParam.append("intWarehouseCode",intWarehouseCode)
                .append("inType",101)
                .append("relDataId",billNo)//关联单据编号,可随机，保证不重复即可
                .append("applyDate",VtuzxUtil.formatYmdHmsF(new Date()))
                .append("operator","系统自动")
                .append("source","OPEN")
                .append("notificationCode","100")
                .append("stockInDetailViews",stockInDetailViews);
        try {
            iJiKeYunApiService.createStockIn(storeDoc,stockParam);
        } catch (Exception e){
            throw new VtuzxException(e.getMessage());
        }
    }

    //传入库
    private  void createPrdInDetail() throws  Exception{
//        List<String> prdInBillNoList = new ArrayList<>();
        List<String> prdInBillNoList = Arrays.asList(
                "CJRK250902073046491"
        );

        for (String prdInBillNo : prdInBillNoList){
            double count = 0d;
            String materialNo = "";
            Iterator<Document> prdInDocItr = prdInDetailDao.findList(Criteria.where("billNo").is(prdInBillNo),null,0,0);
            while (prdInDocItr.hasNext()){
                Document prdInDoc = prdInDocItr.next();
                count += VtuzxUtil.getDouble(prdInDoc,"count");
                materialNo = VtuzxUtil.getString(prdInDoc,"materialNo");
            }

//            double boxCount = VtuzxUtil.getDouble(prdInDoc,"boxCount");


            //获取令牌
            Document storeDoc = storeTokenDao.findOne(Criteria.where("type").is("kingdee"));
            String intWarehouseCode = "XQWG00101";//仓库编码
//            VtuzxMap goodsMap = new VtuzxMap();//getGoodsList方法返回值
//            VtuzxMap goodsParam = new VtuzxMap();
//            goodsParam.append("pageIndex", 0)
//                    .append("pageSize", 1)
//                    .append("goodsNo", materialNo);
//            try {
//                goodsMap = iJiKeYunApiService.getGoodsList(storeDoc, goodsParam);
//            } catch (Exception e) {
//                _logger.info(e.getMessage());
//            }

//            LinkedHashMap result = VtuzxUtil.getObject(goodsMap, "result");
//            LinkedHashMap data = VtuzxUtil.getObject((Map<String, Object>) result, "data");
//            List<LinkedHashMap> goods = VtuzxUtil.getObject((Map<String, Object>) data, "goods");
//            LinkedHashMap goodsResult = goods.get(0);
//            String skuBarcode = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuBarcode");
//            String skuId = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuId");
//            String materialModel = VtuzxUtil.getString((Map<String, Object>) goodsResult, "skuName");//物料规格
            //创建极客云入库单
            VtuzxMap stockParam = new VtuzxMap();//入库参数
            List<VtuzxMap> stockInDetailViews = new ArrayList<>();//入库申请单明细
            stockInDetailViews.add(new VtuzxMap().append("relDetailId", prdInBillNo)
                    .append("skuCount", count).append("isCertified", 1)
                    .append("skuBarcode", materialNo));
            stockParam.append("inWarehouseCode", intWarehouseCode)//仓库
                    .append("inType", 101)
                    .append("relDataId", "补传"+ prdInBillNo + 1)//关联单据编号,可随机，保证不重复即可
                    .append("applyDate", VtuzxUtil.formatYmdHmsF(new Date()))
                    .append("operator", "sunyf")
                    .append("source", "OPEN")
                    .append("notificationCode", "100")
                    .append("stockInDetailViews", stockInDetailViews);
            //创建入库单
            try {
                iJiKeYunApiService.createStockIn(storeDoc, stockParam);
            } catch (Exception e) {
                _logger.info(e.getMessage());
            }
        }


    }

    //导出采购入库列表
//    private VtuzxDownloadBean downloadPurIn() throws Exception {
//        Criteria cond = new Criteria();
//        Iterator<Document> purInDetailItr = purInDetailDao.findList(cond, new Document("createTime", -1), 0, 0);
//
//        List<VtuzxMap> detailList = new ArrayList<>();
//        while (purInDetailItr.hasNext()) {
//            Document purInDoc = purInDetailItr.next();
//            VtuzxMap detailMap = new VtuzxMap(purInDoc);
//
//            detailList.add(detailMap);
//        }
//
//        String timestamp = new SimpleDateFormat("yyMMddHHmmss").format(new Date());
//
//        List<VtuzxExcelSheet> sheets = new ArrayList<>();
//        VtuzxExcelSheet sheet0 = new VtuzxExcelSheet(0, "即时库存");
//        int col = 0;
//        int row = 0;
//        VtuzxExcelRow header0 = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
//                new VtuzxExcelCell(col++, "单号"),
//                new VtuzxExcelCell(col++, "入库时间"),
//                new VtuzxExcelCell(col++, "仓库"),
//                new VtuzxExcelCell(col++, "物料编码"),
//                new VtuzxExcelCell(col++, "物料名称"),
//                new VtuzxExcelCell(col++, "物料规格"),
//                new VtuzxExcelCell(col++, "批次号"),
//                new VtuzxExcelCell(col++, "箱数"),
//                new VtuzxExcelCell(col++, "数量"),
//                new VtuzxExcelCell(col++, "单位")
//        ));
//        sheet0.addRow(header0);
//
//        for (VtuzxMap detail : detailList) {
//
//
//            col = 0;
//            VtuzxExcelRow dataRow = new VtuzxExcelRow(row++, new VtuzxArrayList<>(
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "billNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.formatYmdHmF(VtuzxUtil.getObject(detail, "createTime"))),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "stockNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialName")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "materialSpec")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "batchNo")),
//                    new VtuzxExcelCell(col++, VtuzxUtil.getString(detail, "boxCount")),
//                    new VtuzxExcelCell(col++, new BigDecimal(VtuzxUtil.getString(detail, "count")).setScale(2, RoundingMode.HALF_UP).doubleValue()),
//                    new VtuzxExcelCell(col, VtuzxUtil.getString(detail, "unit"))
//            ));
//            sheet0.addRow(dataRow);
//        }
//
//        sheet0.setColumnWidth(new VtuzxArrayList<>(
//                new int[]{0, 200},
//                new int[]{1, 200},
//                new int[]{2, 200},
//                new int[]{3, 300},
//                new int[]{4, 200},
//                new int[]{5, 200},
//                new int[]{6, 150},
//                new int[]{7, 150},
//                new int[]{8, 80}
//        ));
//
//        sheets.add(sheet0);
//        ByteArrayOutputStream stream = new ByteArrayOutputStream();
//        VtuzxExcel.write(stream, sheets);
//        return new VtuzxDownloadBean("采购入库" + timestamp + ".xlsx", stream);
//    }

    private  void inventoryHistory (String name,ObjectId userId){
        Criteria cond = new Criteria();
//        cond.and("status").is("out");
//        cond.and("stockNo").ne("011");
//        cond.and("boxNo").regex("A01");
        cond.and("createTime").gte(VtuzxUtil.parseDate("2023-08-18 13:34:00")).lte(VtuzxUtil.parseDate("2023-08-18 13:34:59"));
//        long count = inventoryHistoryDetailDao.count(cond);
//        System.out.println(count);
        Iterator<Document> historyItr = inventoryHistoryDetailDao.findList(cond,new Document("createTime",1),0,0);
        List<Document> inventoryList = new ArrayList<>();
        int a = 0;
        while (historyItr.hasNext()){
            Document historyDoc = historyItr.next();
            String status = VtuzxUtil.getString(historyDoc,"status");
            a++;
            if ("in".equals(status)){
                a++;
                String boxNo = VtuzxUtil.getString(historyDoc,"boxNo");
                String stockNo = VtuzxUtil.getString(historyDoc,"stockNo");
                String locationNo = VtuzxUtil.getString(historyDoc,"locationNo");
                String trayNumber = VtuzxUtil.getString(historyDoc,"trayNumber");
                ObjectId _inventoryId = VtuzxUtil.getObject(historyDoc,"_inventoryId");
                Date createTime = VtuzxUtil.getObject(historyDoc,"createTime");
                String rfid = VtuzxUtil.getString(historyDoc,"rfid");
                double count = VtuzxUtil.getDouble(historyDoc,"count");
                List<Document> traceBack = VtuzxUtil.getObject(historyDoc,"traceBack");
                Document detailDoc = new Document();
                detailDoc.append("boxNo",boxNo)
                        .append("rfid",rfid)
                        .append("count",count)
                        .append("traceBack",traceBack)
                        .append("trayNumber",trayNumber)
                        .append("isTaskIn",true)
                        .append("stockNo",stockNo)
                        .append("locationNo",locationNo)
                        .append("status","in")
                        .append("_inventoryId",_inventoryId)
                        .append("createTime",createTime)
                        .append("updateTime",createTime)
                        .append("createUserName","sunyf")
                        .append("_updateUserId",userId)
                        .append("updateUserName","sunyf")
                        .append("_createUserId",userId);
                inventoryList.add(detailDoc);
                if (inventoryList.size() == 2000){
                    inventoryDetailDao.insertMany(inventoryList);
                    inventoryList.clear();
                    System.out.println(a);
                }

            }
            if (inventoryList.size() > 0){
                inventoryDetailDao.insertMany(inventoryList);
            }

//            String outBoxNo = VtuzxUtil.getString(historyDoc,"boxNo");
//            inventoryList.add(outBoxNo);
//            if (inventoryList.size() == 500){
//                inventoryDetailDao.updateManyRedirect(Criteria.where("boxNo").in(inventoryList),new Document( "$set",new Document("status","out")));
//                inventoryList.clear();
//                System.out.println(a);
//            }



        }
//        if (inventoryList.size() > 0){
//            inventoryDetailDao.updateManyRedirect(Criteria.where("boxNo").in(inventoryList),new Document( "$set",new Document("status","out")));
//            System.out.println(a);
//        }

    }


    //调整入库详情表(水带)
    private void prdInChange(ObjectId userId){
        Criteria prdInCond = new Criteria();
        prdInCond.and("createTime").gte(VtuzxUtil.parseDate("2023-11-01 00:00:00")).lte(VtuzxUtil.parseDate("2023-11-01 13:59:59"));
//        prdInCond.and("createUserName").regex("梁");
        prdInCond.and("return").is(true);
//        prdInCond.and("task").is(true);
        prdInCond.and("count").ne(0);

        Iterator<Document> prdIn = prdInDao.findList(prdInCond,null,0,0);
        List<String> billNoList = new ArrayList<>();//需要调整的入库单号
//        billNoList.add("CJRK230818141840525");
        while (prdIn.hasNext()){
            Document prdDoc = prdIn.next();
            String billNo = VtuzxUtil.getString(prdDoc,"billNo");
            if (!billNoList.contains(billNo)){
                billNoList.add(billNo);
            }
        }
        System.out.println("");

        for (String prdInbillNo : billNoList){
            Criteria cond = new Criteria();
            cond.and("return").is(true);
//        cond.and("createTime").gte(VtuzxUtil.parseDate("2023-08-20 00:00:00")).lte(VtuzxUtil.parseDate("2023-08-31 23:59:59"));

            cond.and("billNo").is(prdInbillNo);
            List<Document> prdInDetailBoxList = new ArrayList<>();
            Iterator<Document> prdInItr = prdInDao.findList(cond,new Document("createTime",-1),0,0);
            while (prdInItr.hasNext()){
                Document prdInDoc = prdInItr.next();
                String billNo = VtuzxUtil.getString(prdInDoc,"billNo");//入库单号
                //根据入库单号查询车间收货
                Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("perInBillNo").is(billNo),null,0,0);
                while (prdColDetailItr.hasNext()){
                    Document prdColDetailDoc = prdColDetailItr.next();
                    String prdColBillNo = VtuzxUtil.getString(prdColDetailDoc,"billNo");//车间收货单号
                    String colType = VtuzxUtil.getString(prdColDetailDoc,"colType");
                    String boxNo = VtuzxUtil.getString(prdColDetailDoc,"boxNo");
                    String unit = VtuzxUtil.getString(prdColDetailDoc,"unit");
                    double count = VtuzxUtil.getDouble(prdColDetailDoc,"count");
                    List<Document> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                    //从车间收货外层表获取物料编码等信息
                    Document prdColDoc = prdColDao.findOne(Criteria.where("billNo").is(prdColBillNo));
                    String materialName = VtuzxUtil.getString(prdColDoc,"materialName");
                    String materialNo = VtuzxUtil.getString(prdColDoc,"materialNo");
                    String materialModel = VtuzxUtil.getString(prdColDoc,"materialModel");
                    String batchNo = VtuzxUtil.getString(prdColDoc,"batchNo");
                    String taskBillNo = VtuzxUtil.getString(prdColDoc,"taskBillNo");
                    Document prdInDetailBoxDoc = new Document();
                    prdInDetailBoxDoc.append("boxNo",boxNo)
                            .append("batchNo",batchNo)
                            .append("count",count)
                            .append("unit",unit)
                            .append("colType",colType)
                            .append("createUserName","sunyf")
                            .append("_createUserId",userId)
                            .append("traceBack",traceBack)
                            .append("billNo",billNo)
                            .append("rfid","")
                            .append("materialNo",materialNo)
                            .append("barcodeType","")
                            .append("materialName",materialName)
                            .append("materialSpec",materialModel)
                            .append("taskBillNo",taskBillNo)
                            .append("prdColBillNo",prdColBillNo);
                    prdInDetailBoxList.add(prdInDetailBoxDoc);
                }
            }
            //入库详情表数据
            List<Document> prdInDetailList = new ArrayList<>();
            Iterator<Document> prdInDetailItr = prdInDetailDao.findList(cond,new Document("createTime",-1),0,0);
            while (prdInDetailItr.hasNext()){
                Document prdInDetailDoc = prdInDetailItr.next();
                prdInDetailList.add(prdInDetailDoc);
            }
            for (Document prdInDetailBoxDoc : prdInDetailBoxList){
                String materialNo = VtuzxUtil.getString(prdInDetailBoxDoc,"materialNo");
                String batchNo = VtuzxUtil.getString(prdInDetailBoxDoc,"batchNo");
                double count = VtuzxUtil.getDouble(prdInDetailBoxDoc,"count");
                for (Document prdInDetailDoc : prdInDetailList){
                    String inMmaterialNo = VtuzxUtil.getString(prdInDetailDoc,"materialNo");
                    String inBatchNo = VtuzxUtil.getString(prdInDetailDoc,"batchNo");
                    double inCount = VtuzxUtil.getDouble(prdInDetailDoc,"count");
                    if (materialNo.equals(inMmaterialNo) && batchNo.equals(inBatchNo)){
                        String stockNo = VtuzxUtil.getString(prdInDetailDoc,"stockNo");
                        String locationNo = VtuzxUtil.getString(prdInDetailDoc,"locationNo");
                        Date createTime = VtuzxUtil.getObject(prdInDetailDoc,"createTime");
                        boolean task = VtuzxUtil.getBoolean(prdInDetailDoc,"task");
                        ObjectId _detailId = VtuzxUtil.getObject(prdInDetailDoc,"_id");
                        prdInDetailBoxDoc.append("stockNo",stockNo)
                                .append("locationNo",locationNo)
                                .append("createTime",createTime)
                                .append("updateTime",createTime)
                                .append("task",task)
                                .append("_detailId",_detailId);
                    }
                }
            }
            System.out.println("添加数据");
            prdInDetailBoxDao.insertMany(prdInDetailBoxList);
        }

    }
    //调整入库详情表(成品入库的)
    private void getPrdIn(ObjectId userId){
        Criteria cond = new Criteria();
        cond.and("billNo").is("CJRK230818181712555");
//        cond.and("simple").is(true);
//        cond.and("createTime").gte(VtuzxUtil.parseDate("2023-10-01 00:00:00")).lt(VtuzxUtil.parseDate("2023-10-31 23:59:59"));
        Iterator<Document> prdInDetailitr = prdInDetailDao.findList(cond,new Document("createTime",1),0,0);
        List<Document> prdInDetailBoxList = new ArrayList<>();
        while (prdInDetailitr.hasNext()){
            Document prdInDetailDco = prdInDetailitr.next();
            String billNo = VtuzxUtil.getString(prdInDetailDco,"billNo");//入库单号
            Date updateTime = VtuzxUtil.getObject(prdInDetailDco,"updateTime");
            Date createTime = VtuzxUtil.getObject(prdInDetailDco,"createTime");
            String stockNo = VtuzxUtil.getObject(prdInDetailDco,"stockNo");
            String locationNo = VtuzxUtil.getObject(prdInDetailDco,"locationNo");
            boolean task = VtuzxUtil.getBoolean(prdInDetailDco,"task");
            boolean simple = VtuzxUtil.getBoolean(prdInDetailDco,"simple");
            String materialNo = VtuzxUtil.getString(prdInDetailDco,"materialNo");
            ObjectId _detailId = VtuzxUtil.getObject(prdInDetailDco,"_id");
            Iterator<Document> prdColDetailItr = prdColDetailDao.findList(Criteria.where("perInBillNo").is(billNo),null,0,0);
            while (prdColDetailItr.hasNext()){
                Document prdColDetailDoc = prdColDetailItr.next();
                String materialName = VtuzxUtil.getString(prdColDetailDoc,"materialName");
                String materialModel = VtuzxUtil.getString(prdColDetailDoc,"materialModel");
                String taskBillNo = VtuzxUtil.getString(prdColDetailDoc,"taskBillNo");
                String perInBillNo = VtuzxUtil.getString(prdColDetailDoc,"perInBillNo");
                String prdColBillNo = VtuzxUtil.getString(prdColDetailDoc,"billNo");
                String batchNo = VtuzxUtil.getString(prdColDetailDoc,"batchNo");
                String colType = VtuzxUtil.getString(prdColDetailDoc,"colType");
                String boxNo = VtuzxUtil.getString(prdColDetailDoc,"boxNo");
                String unit = VtuzxUtil.getString(prdColDetailDoc,"unit");
                double count = VtuzxUtil.getDouble(prdColDetailDoc,"count");
                List<Document> traceBack = VtuzxUtil.getObject(prdColDetailDoc,"traceBack");
                Document prdInDetailBoxDoc = new Document();
                prdInDetailBoxDoc.append("materialNo",materialNo)
                        .append("materialName",materialName)
                        .append("materialSpec",materialModel)
                        .append("boxNo",boxNo)
                        .append("batchNo",batchNo)
                        .append("count",count)
                        .append("unit",unit)
                        .append("createUserName","sunyf")
                        .append("_createUserId",userId)
                        .append("stockNo",stockNo)
                        .append("locationNo",locationNo)
                        .append("task",task)
                        .append("simple",simple)
                        .append("traceBack",traceBack)
                        .append("billNo",billNo)
                        .append("createTime",createTime)
                        .append("updateTime",updateTime)
                        .append("taskBillNo",taskBillNo)
                        .append("prdColBillNo",prdColBillNo)
                        .append("_detailId",_detailId);
                if ("return".equals(colType)){
                    prdInDetailBoxDoc.append("return",true)
                            .append("colType",colType);
                }
                prdInDetailBoxList.add(prdInDetailBoxDoc);
                if (prdInDetailBoxList.size() == 1000){
                    prdInDetailBoxDao.insertMany(prdInDetailBoxList);
                    prdInDetailBoxList.clear();
                    System.out.println("添加1000条数据");
                }
            }
        }
        if(prdInDetailBoxList.size() > 0){
            prdInDetailBoxDao.insertMany(prdInDetailBoxList);
            System.out.println("添加数据");
        }

    }
    //重复入库删除即时库存箱号
    private void changePrdInBox(){
        Criteria cond = new Criteria();
//        cond.and("billNo").is("CJRK231101201234326");
        Iterator<Document> prdInDetailBoxItr = prdInDetailBoxDao.findList(cond,null,0,0);
        List<String> boxNoList = new ArrayList<>();
        while (prdInDetailBoxItr.hasNext()){
            Document prdInBoxDoc = prdInDetailBoxItr.next();
            String boxNo = VtuzxUtil.getString(prdInBoxDoc,"boxNo");
            boxNoList.add(boxNo);
        }
        for (String boxNo : boxNoList){
            double count = inventoryDetailDao.count(Criteria.where("boxNo").is(boxNo));
            if (count == 2){
                System.out.println("删除" + boxNo);
                inventoryDetailDao.deleteOne(Criteria.where("boxNo").is(boxNo));
            }
        }
    }
    }